關於C++ 的繼承 多態 和與之相關的對象模型知識,各個書本上很多了
今天來看幾個對象模型的問題 在實踐中理解C++ 對象模型:
1、 在下列代碼中:
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;
}
`
nt main()
{
Fun();
return 0;
}
選項:
A pA、pB、pC的取值相同
B、 pC 和 pA不相同
C、pB和pC不相同
D、pC不等於pA,也不等於pB。
解析:在繼承體系中,當用一個基類的指針或引用指向派生類對象時(賦值兼容規則),這時會將派生類對象的地址偏移一個合適偏移量,因爲派生類對象也是一個基類對象,實際也就是讓基類指針指向派生類中屬於基類的那部分。所以一般先繼承的那個類(基類)的指針和派生類的對象的地址相等。
2、 類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 類毫無關係,則pd->fun();一定到用的是派生類的fun()函數,
但是pb又是被強制指向A類對象的,
那麼肯定會打印前A類對象前四個字節的值,也就是1。(因爲B類大小就是4個字節)。因爲B類的大小的4個字節,所以B類的指針管理的內存範圍是4個字節。
3、 當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;
}
這裏pd->fun()還是調用的派生類的fun函數,可這時派生類繼承了基類,所以派生類的指針管理的範圍就是 (4 + 8) = 12個字節。而繼承基類A的派生類B 並沒有被定義,m_c的空間還沒有開闢出來,所以這時候打印m_c結過時崩潰或隨機數。
4、當A類的fun函數被聲明爲虛函數時,並且定義了B類時。
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類
B* pb = (B*)(&a);//切記:這裏是派生類的指針指向基類對象,不會形成多態
pb->fun();//在A類的虛表內找fun函數(調用的是A類的fun函數),輸出1和2
A* pa = (A*) &b;
pa->fun();//這裏形成了多態 打印 。
}
這裏雖然定義了B類,但是沒有形成多態,pb實際上指向的是A的空間pd->fun()是在A的虛表中找vptr 函數指針 所以調用的是A的fun()函數。
4、 如果是基類fun函數是普通函數,派生類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* pa = (A*) &b;
pa->fun();
}
int main()
{
Fun();
system("pause");
return 0;
}
這時候仍然沒有形成多態, 但是B類的fun()函數是虛函數,pb指向&a.所以pb->fun()這一句,會在 A對象的前四個字節中尋找虛表,所以他把m_a當成虛表使得程序崩潰。
這裏是因爲沒有形成多態,所以pb雖然指向&a但是pb的類型是B* pb會按照B類的結構來解釋在A類內存空間中的數據。