#include <iostream>
#include <string>
using namespace std;
/*
*關閉內存對其,我們能夠看到類實際的空間大小
*(從中我們可以看出,不光是結構體存在內存對齊,類也存在內存對齊)
*/
#pragma pack(1)
class A{
private:
public:
int a;
A(){};
A(int a):a(a){};
void show(){cout << "A class, a = " << a << endl;};
};
class B:public A{
private:
public:
int b;
B(){};
B(int a, int b)
:A(a),b(b){};
virtual void show(){cout << "B class, b = " << b << endl;};
};
class C:public B{
private:
public:
int c;
C(){};
C(int a, int b, int c)
:B(a, b), c(c){};
// C中並沒有將show聲明爲virtual,但是由於其與B中聲明的virtual show同名,所以也會成爲虛函數。
// 也就是說如果A中將show聲明爲虛函數,那麼之後B,C不用顯式聲明也會設置爲virtual
void show(){cout << "C class, c = " << c << endl;};
};
/*
* MemA 描述了類A的內存情況,其中僅保存了一個int數據,(這是一個沒有虛函數的類的內存情況)
* 其中沒有vptr是因爲A沒有定義虛函數,所以我們沒有必要浪費那個空間去存儲vptr
* 但是這樣卻對導致A, B, C的數據佈局不一致。
* A offset 0爲數據, 而B, C的offset 8纔開始存儲數據。
*/
struct MemA
{
int a;
};
/*
* MemC 描述了類C的內存情況,其offset +0存儲虛函數表,+8存儲A.a
*/
struct MemC
{
void * vptr; // 這裏使用一個(void *)來存儲vptr
int a; // 分別對應 A.a , B.b, C.c
int b;
int c;
};
int main(){
/* 各個類的內存大小 */
cout << "sizeof A = " << sizeof(A) << endl; /* 4 bytes */
cout << "sizeof B = " << sizeof(B) << endl; /* 16 bytes*/
cout << "sizeof C = " << sizeof(C) << endl; /* 20 bytes*/
/* 使用結構體修改類中的數據 */
C c(1, 2, 3);
cout << c.a << " " << c.b << " " << c.c << endl; /*1 2 3*/
MemC *pc = (MemC *)&c; // 使用結構體指向類的內存空間
cout << pc->a << " " << pc->b << " " << pc->c << endl; /* 1 2 3 */
pc->a = 100; // 使用結構體去修改類中的數據
cout << c.a << " " << c.b << " " << c.c << endl; /* 100 2 3*/
/* 類指針的轉換問題 */
A *pa1, *pa2;
pa1 = (A *)&c; // 此處並不是將&c的值直接賦值給pa1,而是向後移動8 bytes,保證了C*向A*的正確轉換
pa2 = (A *)(void *)&c; // 此處並不會,因爲這個轉換隻是void* 向 A*進行
cout << pa1 << " " << "pa1->a = " << pa1->a << endl; // 0x7ffcc158f378 pa1->a = 100
cout << pa2 << " " << "pa2->a = " << pa2->a << endl; // 0x7ffcc158f370 pa2->a = 1389382912
/* 靜態聯編, 動態聯編 */
C *ppc = (C *)&c;
ppc->show(); // C class, c = 3, 此處的調用順序是,先訪問vptr,通過vptr找到應該調用的函數地址
pc->vptr = nullptr; // 此處我們去去破壞vptr。
c.show(); // C class, c = 3 , 此處的調用順序是,在編譯階段直接將c.show()指向C::show(),因爲這個可以在編譯期間確定
(&c)->show(); // C class, c = 3, 此處同上,也在編譯期間就能確定,這種方式可以加速執行速度(少了以此間接尋址過程)
ppc->show(); // Segmentation fault (core dumped)
}
運行結果