前言
很多人会误以为C++的效率低于C,实质上C++ 相对C语言并没有过多的开销,由于对对象模型不理解,很多人会误以为C++ 编译器会在背后偷偷干一些事情。 对象布局不是C++的语言标准,所以每个编译器可以有自己的实现,但很多时候各编译器的实现是相同的。讨论C++的对象布局,可以了解其背后做了什么事情,理解对象的开销。
对象
对象封装后的成本
通过class封装函数和成员变量,并不会增加额外的成本。如下代码,和C定义一个struct变量和一个prt函数的成本是一致的。class的data member和struct一样组织,member function,是所有的object共用的。
class C
{
private:
int a;
char b;
public:
C()
{
}
void prt()
{
}
}
对象模型
C++中有两种data member:static 和 nonstatic,三种class member functions:static、nonstatic 和 virtual。
nonstatic data member 置于每个class object中,static data members则放在所有的class object之外,static 和 nonstatic function member 也被放在所有的class object之外,virtual functions则通过以下两个步骤支持:
(1)通过virtual table保存指向各个virtual function的指针
(2)每个object起始地址添加一个指向virtual table的指针: vptr。virtual table 的首位置保存了对象类型。从而实现了运行时类型识别。
继承
包括单一继承、多重继承(最好是少用)、虚继承(共享继承,从来没见代码中实际用过)。C++的派生类data member和基类的data member是紧凑存储的,base class data member 直接放在deriverd class中,从而达到更高效率。
默认构造函数的构建
什么时候编译器会自动添加一个默认构造函数?看下面的代码
class Foo()
{
public:
int val;
Foo *pnext;
};
void foo_bar()
{
Foo bar;
if (bar.val || bar.pnext)
{
...
}
}
没有任何构造函数的时候,编译器会生成一个默认构造函数,默认构造函数没做什么有效的事情,上面例子中,需要生成一个构造函数将变量初始化为0才行。生成的默认构造函数会包含必要的代码。Bar的默认构造函数会自动调用Foo的默认构造函数。
class Bar
{
Foo f;
char *p;
};
Copy constructor的构建
如果没有定义拷贝构造函数,会生成一个默认拷贝构造函数。默认拷贝构造函数,会直接复制内建类型的值,对于嵌套的object,则采用递归的方式执行成员初始化。例如:
class C
{
private:
char *p;
int a;
Foo f;
};
class C的默认拷贝构造函数,会直接复制p和a的值,然后递归初始化f。