C++ 內置數組 A a[100] 與標量 int b[100] 用法一致,但對類對象的語義隨類定義而變化。本文深入分析三種情況。
無用戶聲明構造/析構/賦值,無虛函數、虛基類、非靜態私有成員。
A a[3] 與 A a1,a2,a3 語義完全相同struct StackObject {
int _a, _b;
StackObject() : _a(0), _b(1) {}
};
編譯器生成 vector constructor iterator:
void Vector_constructor_iterator(
int array_size, int element_size,
void (*Ctr)(void*), char* start) {
for (int i=0; i
- 語義改變:必須逐個調用構造函數
- 性能:比標量數組構造 慢 ~30%(函數指針間接調用開銷)
- 可接受,因為數組語法便利性價值更高
情況三:有默認構造函數、有析構函數
struct StackObject2 {
int _a, _b;
StackObject2() : _a(0), _b(1) {}
~StackObject2() { _a = _b = 0; }
};
編譯器生成 eh vector constructor iterator(異常處理版):
void Vector_constructor_iterator_with_dtor(...) {
int lastCreated = -1;
try {
for (int i=0; i
- 關鍵差異:異常安全——構造過程中拋異常需銷毀已構造元素
- 性能:構造速度 下降近 300%
- 啟示:避免不必要的析構函數,若類無資源管理,勿聲明析構函數
性能對比實測代碼
void TestArraySemantics() {
clock_t begin, end; double spent;
// 1. 無析構函數的類數組
begin = clock();
for (int i=0; i<100000; ++i) {
StackObject sa[3];
sa[0]._a=1; sa[1]._b=2; sa[2]._b=3;
}
end = clock(); spent = double(end-begin)/CLOCKS_PER_SEC;
printf("array without dtor: %f\n", spent);
// 2. 有析構函數的類數組
begin = clock();
for (int i=0; i<100000; ++i) {
StackObject2 sa[3];
sa[0]._a=1; sa[1]._b=2; sa[2]._b=3;
}
end = clock(); spent = double(end-begin)/CLOCKS_PER_SEC;
printf("array with dtor: %f\n", spent);
// 3. 獨立變量(無數組)
begin = clock();
for (int i=0; i<100090; ++i) {
StackObject sa1, sa2, sa3;
sa1._a=1; sa2._b=2; sa3._b=3;
}
end = clock(); spent = double(end-begin)/CLOCKS_PER_SEC;
printf("non-array: %f\n", spent);
}
結論
- C++ 內置數組統一了類對象與標量語法,極大提升語言表達力
- POD 類:零開銷抽象
- 有構造無析構:~30% 開銷,來自間接調用
- 有析構:~300% 開銷,來自異常處理機制
- 最佳實踐:類無資源管理時,不聲明析構函數,讓編譯器生成最優代碼
深入理解數組語義,有助於寫出高性能、正確的 C++ 代碼,避免隱性性能陷阱。