——————————————————我是分割線—————————————————————
1、空類,空類單繼承,空類多繼承的sizeof
- #include <iostream>
- using namespace std;
- class Base1
- {
- };
- class Base2
- {
- };
- class Derived1:public Base1
- {
- };
- class Derived2:public Base1,public Base2
- {
- };
- int main()
- {
- Base1 b1;
- Base2 b2;
- Derived1 d1;
- Derived2 d2;
- cout<<"sizeof(Base1) = "<<sizeof(Base1)<<"
sizeof(b1) = "<<sizeof(b1)<<endl;
- cout<<"sizeof(Base2) = "<<sizeof(Base2)<<"
sizeof(b2) = "<<sizeof(b2)<<endl;
- cout<<"sizeof(Derived1) = "<<sizeof(Derived1)<<"
sizeof(d1) = "<<sizeof(d1)<<endl;
- cout<<"sizeof(Derived2) = "<<sizeof(Derived2)<<"
sizeof(d1) = "<<sizeof(d1)<<endl;
-
- return 0;
- }
sizeof(Base2) = 1 sizeof(b2) = 1
sizeof(Derived1) = 1 sizeof(d1) = 1
sizeof(Derived2) = 1 sizeof(d1) = 1
可以看出所有的結果都是1。
假設我們有這樣的一個類:
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
當我們定義一個這個類的實例,Base b時,其b中成員的存放如下:
虛函數表的最後多加了一個結點,這是虛函數表的結束結點,就像字符串的結束符“\0”一樣,其標誌了虛函數表的結束。這個結束標誌的值在不同的編譯器下是不同的。在WinXP+VS2003下,這個值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,這個值是如果1,表示還有下一個虛函數表,如果值是0,表示是最後一個虛函數表。
因爲對象b中多了一個指向虛函數表的指針,而指針的sizeof是4,因此含有虛函數的類或實例最後的sizeof是實際的數據成員的sizeof加4。
下面將討論針對基類含有虛函數的繼承討論
(1)在派生類中不對基類的虛函數進行覆蓋,同時派生類中還擁有自己的虛函數,比如有如下的派生類:
class Derived: public Base
{
public:
virtual void f1() { cout << "Derived::f1" << endl; }
virtual void g1() { cout << "Derived::g1" << endl; }
virtual void h1() { cout << "Derived::h1" << endl; }
};
基類和派生類的關係如下:
當定義一個Derived的對象d後,其成員的存放如下:
可以發現:
1)虛函數按照其聲明順序放於表中。
2)父類的虛函數在子類的虛函數前面。
此時基類和派生類的sizeof都是數據成員的sizeof加4。
(2)在派生類中對基類的虛函數進行覆蓋,假設有如下的派生類:
class Derived: public Base
{
public:
virtual void f() { cout << "Derived::f" << endl; }
virtual void g1() { cout << "Derived::g1" << endl; }
virtual void h1() { cout << "Derived::h1" << endl; }
};
基類和派生類之間的關係:其中基類的虛函數f在派生類中被覆蓋了
當我們定義一個派生類對象d後,其d的成員存放爲:
可以發現:
1)覆蓋的f()函數被放到了虛表中原來父類虛函數的位置。
2)沒有被覆蓋的函數依舊。
這樣,我們就可以看到對於下面這樣的程序,
Base *b = new Derive();
b->f();
由b所指的內存中的虛函數表的f()的位置已經被Derive::f()函數地址所取代,於是在實際調用發生時,是Derive::f()被調用了。這就實現了多態。
(3)多繼承:無虛函數覆蓋
假設基類和派生類之間有如下關係:
對於子類實例中的虛函數表,是下面這個樣子:
我們可以看到:
1) 每個父類都有自己的虛表。
2) 子類的成員函數被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)
由於每個基類都需要一個指針來指向其虛函數表,因此d的sizeof等於d的數據成員加3*4=12。
(4)多重繼承,含虛函數覆蓋
假設,基類和派生類又如下關係:派生類中覆蓋了基類的虛函數f
下面是對於子類實例中的虛函數表的圖:
我們可以看見,三個父類虛函數表中的f()的位置被替換成了子類的函數指針。這樣,我們就可以任一靜態類型的父類來指向子類,並調用子類的f()了。如:
Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()
b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()
3、一個關於含虛函數及虛繼承的sizeof計算
- #include
<iostream>
- using namespace std;
- class Base
- {
- public:
- virtual void f();
- virtual void g();
- virtual void h();
- };
- class Derived1:public Base
- {
- public:
- virtual void f1();
- virtual void g1();
- virtual void h1();
- };
- class Derived2:public Base
- {
- public:
- virtual void f();
- virtual void g1();
- virtual void h1();
- };
- class Derived3:virtualpublic Base
- {
- public:
- virtual void f1();
- virtual void g1();
- virtual void h1();
- };
- class Derived4:virtualpublic Base
- {
- public:
- virtual void f();
- virtual void g1();
- virtual void h1();
- };
- class Derived5:virtualpublic Base
- {
- public:
- virtual void f();
- virtual void g();
- virtual void h();
- };
- class Derived6:virtualpublic Base
- {
- };
- int main()
- {
- cout<<sizeof(Base)<<endl;//4
- cout<<sizeof(Derived1)<<endl;//4
- cout<<sizeof(Derived2)<<endl;//4
- cout<<sizeof(Derived3)<<endl;//12
- cout<<sizeof(Derived4)<<endl;//12
- cout<<sizeof(Derived5)<<endl;//8
- cout<<sizeof(Derived6)<<endl;//8
- return 0;
- }