1、继承体系下的对象构造
当我定义一个object如下
T object;
如果T有一个默认构造函数,它会被调用。
比较不明显的是构造函数内部有大量的隐藏代码,因为编译器会扩充构造函数,一般而言编译器所做的扩充如下:
- 记录在成员初始化列表中的数据成员初始化操作会被放到构造函数体中,并以成员的声明顺序为顺序。
- 如果成员没有在初始化列表中,但是它有一个默认构造函数,那么它的默认构造函数也要被调用
- 如果有虚函数,须在构造函数里设置虚指针。
- 如果有基类并且基类有构造函数,基类的构造函数必须被调用,以声明顺序为顺序,和初始列表的顺序无关。
- 虚基类的构造函数也必须被调用,顺序由左向右
2、虚指针(vptr)初始化
如果在构造函数里调用虚函数,那么调用的将是当前类里的那个函数,不会调用派生类里的虚函数。
原因是:vptr的设置时间点是在 所有基类构造完之后,用户代码之前。
下面是类A、B、C的代码:
#include<iostream>
class A
{
public:
A() { std::cout << "A:" << size() << std::endl; }
virtual int size() { return sizeof(A); }
};
class B : public A
{
public:
B() { std::cout << "B:" << size() << std::endl; }
virtual int size() { return sizeof(B); }
int b;
};
class C : public B
{
public:
C() { std::cout << "C:" << size() << std::endl; }
virtual int size() { return sizeof(C); }
int c;
};
int main()
{
A* p = new C;
delete p;
return 0;
}
执行结果:
A:4
B:8
C:12
2.1、构造函数的执行顺序
- 在派生类中,所有基类构造会被调用。
- 上述完成后,初始化vptr。
- 如果有初始化列表的话,将初始化列表扩展到构造函数中。
- 最后执行程序员提供的代码
3、对象复制
一个类的拷贝复制操作,在以下情况下不会表现出按位拷贝的语意:
- 当类中有一个成员,而其有一个拷贝赋值函数时。
- 当类有一个基类,而其有一个拷贝赋值函数时。
- 当类中声明了一个虚函数时。
- 当类有虚基类时,
4、析构
如果类没有定义析构函数,那么只有在类内的成员或基类拥有析构函数时,编译器才会合成一个析构出来。
4.1、析构函数的执行循序
- 析构函数本体的代码
- 如果成员对象有析构函数,那么他们会以与声明顺序相反的循序执行。
- 如果对象内有虚函数,那么重新设之vptr,指向适当的基类的虚表
- 如果有任何的非虚基类拥有析构函数,那么以声明顺序相反的顺序执行。
- 如果有任何虚基类拥有析构函数,那么他会以构造顺序相反的循序执行