前言:C++在布局以及存取时间上的额外负担是由Virtual引起,包括:
(1)Virtual function 机制 用以支持一个有效率的执行期绑定"runtime bingding"
(2)Virtual base class 用以实现“多次出现在继承体系中的baseclass,有一个单一而被共享的实体”
1. 比较常用的一个C++对象模型
一种C++的设计模式如下
2. 一般一个类对象需要占多大的内存空间
举例,一个类如下:
class ZooAnimal
{
public:
ZooAnimal( );
virtual~ZooAnimal();
// ..............
virtual void rotate();
protected:
int loc;
string name;
};
ZooAnimal za("Zoey");
ZooAnimal *pza = &za;
它们的可能布局如下(这里将string堪称传统的8-bytes,包括4-bytes的字符指针和一个用来表示子符长度的整数):
(补充于2012-05-15:关于内存布局的更深一步的理解请看下一篇:深度探索C++对象模型之第三章:data语义学中对象布局。)
如果我们把上述类中虚函数删除改为:
class ZooAnimal
{
public:
protected:
int loc;
string name;
};
那么类对象实际上将只占4-bytes + 8-bytes = 12 bytes 原
因是类中没有虚函数,编译器将不会生成指向虚函数的指针(见(
点击打开链接)
中“带有虚基类的类”编译器要做的事)
假定我们加上继承,类如下:
class Bear :public ZooAnimal
{
public:
Bear();
~Bear();
void rotate();
virtual void dance();
// ............
protected:
enum Dances{ ........} ;
Dances Dances_known;
int cell_block;
};
Bear b("Yogi");
Bear *pb = &b;
Bear &rb = *pb;
其可能的内存布局如下:(大小为ZooAnimal的16 bytes加上Bear的8 bytes)
疑问: 派生类中的内存布局中怎么没有出现自身的virtual指针。比如以上类中dance()函数为虚函数,但是怎么在内存中表现,待以后章节解答。
(补充:于2012-05-10)上述问题已经有了答案:
我们定义两个类:
class ZooAnimal
{
public:
ZooAnimal();
virtual ~ZooAnimal();
virtual void animate();
virtual void draw();
// ....
private:
// ZooAnimal的animate()和draw()所需要的数据
};
class Bear: public ZooAnimal
{
public:
Bear();
void animate(); // 虽然没有显式表明是virtual,但是从基类继承virtual属性
void draw(); // 虽然没有显式表明是virtual,但是从基类继承virtual属性
virtual void dance();
// ....
private:
// Bear的animate()和draw()以及dance()需要的数据
};
Bear yogi;
ZooAnimal franny = yogi ; // 注释:这会发生切割行为
上述两个对象的可能内存布局如下;
Bear类实例yogi 也只维护了一个虚函数指针,指向的虚基表中除了包含从基类继承来的虚函数部分,还包括自己本身的虚函数,当发生ZooAnimal franny = yogi ; 等行为时,默认的复制构造函数会将虚基表进行切割,如上图中的 franny 对象。