CPP虛函數分析

#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)


}

運行結果
運行結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章