決定C ++中對象的大小的因素:
1.所有非靜態數據成員的大小 |
2.數據成員的順序 |
3.字節對齊或字節填充 |
4.其直接基類的大小虛函數的存在 |
5. 正在使用的編譯器 |
6.繼承模式(虛擬繼承) |
一、使用sizeof計算類對象所占空間大小
需要注意,對類做sizeof運算時,並不是簡單地把各個成員所占的內存數量相加。需要注意成員順序不同,可能需要字節補齊。
編程實例:
#include <iostream>
using namespace std;
class A
{
public:
int i; //int占用4個字節
};
class B
{
public:
char ch; //char占用1個字節
};
class C
{
public:
int i;
short j;//short占用2個字節
};
class D //D共占用8個字節
{
public:
int i; //int占用4個字節
short j; //short和char共占用3個字節,由於這里最寬的
char ch; //基本類型是int的4個字節,再填充一個字節湊成4的倍數
};
class E //E共占用8個字節
{
public:
int i;
int ii; //兩個int共占用8個字節
short j; //1個short + 2個char = 4個字節,剛好是最寬
char ch; //基本類型int的大小的整數倍,不需要再填充字節
char chr;
};
int main()
{
cout << "sizeof(A) = " << sizeof(A) << endl; //4
cout << "sizeof(B) = " << sizeof(B) << endl; //1
cout << "sizeof(C) = " << sizeof(C) << endl; //8
cout << "sizeof(D) = " << sizeof(D) << endl; //8
cout << "sizeof(E) = " << sizeof(E) << endl; //12
getchar();
return 0;
}
小結:
1.類中的數據成員順序不同,類所占的內存大小可能不同; |
2.注意需要 字節對齊或字節填充 的情況; |
3.派生類的內存大小需要加上基類的內存大小。 |
拓展知識:關於字節對其你和字節填充
編程實例:
class C {
char c;
int int1;
int int2;
int i;
long l;
short s;
};
分析:
這個類的大小是24字節。盡管char c只消耗1個字節,但將為它分配4個字節,剩下的3個字節將被浪費(留空)。這是因為下一個成員是int,它占用4個字節。如果我們不進入下一個(4)字節來存儲這個整數成員,那么這個整數的內存訪問/修改周期將是2個讀周期。所以編譯器會為我們做這個補位。
圖解:
二、使用sizeof計算含有虛函數的類對象的空間大小
虛函數的存在將在類中添加4個字節的虛擬表指針,這將被添加到類的大小。 同樣,在這種情況下,如果類的基類已經直接或通過其基類具有虛函數,那么這個額外的虛函數將不會添加任何類的大小。 虛擬表指針在類層次結構中是通用的。
編程實例:
#include <iostream>
using namespace std;
class Base //Base占用的內存大小為4,即1個int
{
public:
Base(int x) :a(x)
{
}
void print() //函數不占用內存
{
cout << "base" << endl;
}
private:
int a;
};
class Derived :public Base //Derived的內存大小=Base的大小+Derived中的一個int,
{ //即4 + 4 = 8
public:
Derived(int x) :Base(x - 1), b(x)
{
}
void print()
{
cout << "derived" << endl;
}
private:
int b;
};
class A //A共占用8個字節
{
public:
A(int x) :a(x)
{
}
virtual void print() //虛函數產生一個隱含的虛表指針成員,占4個字節
{
cout << "A" << endl;
}
private:
int a; //占4個字節
};
class B :public A //b所占內存 = A所占內存 + 4
{
public:
B(int x) :A(x - 1), b(x)
{
}
virtual void print()
{
cout << "B" << endl;
}
private:
int b; //占4個字節
};
int main()
{
Base obj1(1);
cout << "size of Base obj is " << sizeof(obj1) << endl; //4
Derived obj2(2);
cout << "size of Derived obj is " << sizeof(obj2) << endl; //8
A a(1);
cout << "size of A obj is " << sizeof(a) << endl; //8
B b(2);
cout << "size of B obj is " << sizeof(b) << endl; //12
getchar();
return 0;
}
小結:
1. 普通函數不占用內存; |
2.只要有虛函數就會占用一個指針大小的內存,原因是系統多用一個這鎮維護這個類的虛函數表。 |
三、使用sizeof計算虛擬繼承的類對象的空間大小
要點:在C++中,有時由於某些原因,我們不得不使用虛擬繼承。當我們使用虛擬繼承時,在該類中,虛擬基類指針將會有4個字節的開銷。
編程實例:
#include <iostream>
using namespace std;
class A //空類的大小不為零,一般來說它是1個字節,
{ //確保兩個不同的對象具有不同的地址是非零的
};
class B //1個字節
{
};
class C :public A, public B { //1個字節
};
class D :virtual public A { //虛函數的存在將在類中添加4個字節
//的virtual table pointer
};
class E :virtual public A, virtual public B { //虛繼承A有4個字節+
//虛繼承B有4個字節
};
class F
{
public:
int a; //占4個字節
static int b; //靜態成員的空間不在類的實例中,
//而是像全局變量一樣在靜態存儲區
};
int F::b = 10; //在類外初始化靜態成員
int main()
{
cout << "sizeof(A) = " << sizeof(A) << endl; //1
cout << "sizeof(B) = " << sizeof(B) << endl; //1
cout << "sizeof(C) = " << sizeof(C) << endl; //1
cout << "sizeof(D) = " << sizeof(D) << endl; //4
cout << "sizeof(E) = " << sizeof(E) << endl; //8
cout << "sizeof(F) = " << sizeof(F) << endl; //4
getchar();
return 0;
}
知識擴展:為什么C ++中空類的大小不為零?
例如:
#include<iostream>
using namespace std;
class Empty {};
int main()
{
cout << sizeof(Empty);
return 0;
}
輸出結果:
1 |
分析:
空類的大小不為零。一般是1個字節。確保兩個不同的對象具有不同的地址是非零的。
例如:
#include<iostream>
using namespace std;
class Empty { };
int main()
{
Empty a, b;
if (&a == &b)
cout << "a 和 b的地址相同 " << endl;
else
cout << "a 和 b的地址不同 " << endl;
return 0;
}
輸出結果:
a 和 b的地址不同 |
還有另外一種情況,你可能會感到奇怪:
#include<iostream>
using namespace std;
class Empty { };
class Derived: Empty { int a; };
int main()
{
cout << sizeof(Derived);
return 0;
}
輸出:
4 |
為什么是4,而不是5?
原因是C++有一條規則表明空基類不需要用單獨的字節表示。因此編譯器可以在空基類的情況下自由優化。
<本文完>
參考資料:
1)https://www.geeksforgeeks.org
2)https://www.cprogramming.com
3)《C和C++程序員面試秘笈》