1. 程式人生 > >關於物件模型的幾個面試題(很基礎,但坑較多)

關於物件模型的幾個面試題(很基礎,但坑較多)

可能是因為光是看書,關於看到的知識點也不會去深思各種各樣的場景去構造,導致遇到一些很基礎的問題都會犯二,寫此篇部落格,特警示自己:
學知識,最重要的是思考!!!

如果關於物件模型你還有那麼一點興趣,那麼可以繼續向下看,看看自己是否可以完全答對。。。

1.面試題(選擇題):

下面我貼出的是主要的程式碼片,標頭檔案以及using namespace std;需要自行新增哦!

class A
{
public :
    A()
        :_a(1)
    {}
    virtual void FunTestA()
    {}
    int _a;
};

class B
{
public
: B() :_b(2) {} virtual void FuntestB() {} int _b; }; class C :public A, public B { public : C() :A() , B() , _c(3) {} int _c; }; void Fun() { C c; cout << sizeof(c) << endl; A* pA = &c; B* pB = &c; C* pC = &c; } int
main() { Fun(); return 0; }

選項:
A pA、pB、pC的取值相同
B、 pC 和 pA不相同
C、pB和pC不相同
D、pC不等於pA,也不等於pB。

這裡寫圖片描述

C類物件模型:
這裡寫圖片描述

解析:在繼承體系中,當用一個基類的指標或引用指向派生類物件時(賦值相容規則),這時會將派生類物件的地址偏移一個合適偏移量,因為派生類物件也是一個基類物件,實際也就是讓基類指標指向派生類中屬於基類的那部分。所以一般先繼承的那個類(基類)的指標和派生類的物件的地址相等。不知道你有沒有聽懂呢?(無奈臉)

2.問下列程式會輸出什麼?
1)A類和B類是兩個不相關的類

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    void fun()
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B
{
public :
    B()
    {
        m_c = 3;
    }

    void fun()
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B* pb = (B*)(&a);
    pb->fun();
}

int main()
{
    Fun();
    return 0;
}

A類和B類沒有關係,所以pb肯定呼叫的是B類的fun函式,但是pb又是被強制指向A類物件的,
那麼肯定會列印前A類物件前四個位元組的值,也就是1。(因為B類大小就是4個位元組)。

圖形解析如下:
這裡寫圖片描述

2)B類公有繼承自A類時,程式會輸出什麼?

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    void fun()
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B:public A
{
public :
    B()
        :A()
    {
        m_c = 3;
    }

    void fun()
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B* pb = (B*)(&a);//切記:這裡是派生類的指標指向基類物件
    pb->fun();
}

int main()
{
    Fun();
    return 0;
}

解析:pb仍然會去呼叫B類的fun函式,而B類的fun函式需要列印m_c的值,物件a中並沒有m_c,那塊空間未定義,所以程式最終輸出隨機值或者程式崩潰。

圖解如下:
這裡寫圖片描述

3).B公有繼承自A,且將A中的fun函式定義為虛擬函式後
輸出結果為: 1 2

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    virtual void fun()//虛擬函式
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B:public A
{
public :
    B()
        :A()
    {
        m_c = 3;
    }

    void fun()//重寫
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B b;
    B* pb = (B*)(&a);//切記:這裡是派生類的指標指向基類物件,不會形成多型
    pb->fun();//在A類的虛表內找fun函式(呼叫的是A類的fun函式),輸出1和2
}

int main()
{
    Fun();
    return 0;
}

物件模型:

這裡寫圖片描述

4)B公有繼承自A,且A中的fun為普通函式,僅將B類fun函式定義為虛擬函式.
輸出什麼?——–>程式崩潰

class A
{
public :
    A()
    {
        m_a = 1;
        m_b = 2;
    }

    void fun()//普通函式
    {
        cout << m_a <<"  "<< m_b << endl;
    }
private :
    int m_a;
    int m_b;
};

class B:public A
{
public :
    B()
        :A()
    {
        m_c = 3;
    }

    virtual void fun()//虛擬函式
    {
        cout << m_c << endl;
    }

private :
    int m_c;
};

void Fun()
{
    A a;
    B b;
    B* pb = (B*)(&a);
    pb->fun();//要去a物件的前四個位元組取虛表地址,從而去找fun函式,可惜a物件中並沒有虛表,所以程式崩潰
}

int main()
{
    Fun();
    return 0;
}

這裡寫圖片描述

不知道你看完之後是什麼感受?你有沒有全部答對呢?如果全部答對了,那麼說明你對於繼承/多型理解的特別透徹了,希望你不要像我一樣慘~