——————————————————我是分割线—————————————————————
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;
- }