2.1 Default Constructor建构操作
全局对象的内存(数据区)会在程序激活的时候清为0,局部对象的内存(栈区)以及堆对象的内存(自由存储区)都不一定清0,其内存为上次使用后的遗迹。
根据C++ Standard,对于类X,如果没有任何用户声明的构造函数,那么编译器将会隐式地产生一个默认构造函数,但不作任何动作,被喻为trivial构造函数,与之相对应的,编译器隐式产生的有其他动作发生的默认构造函数被喻为nontrivial构造函数。下面讨论产生nontrivial构造函数的四种情况:(问题:关于C++标准与ARM中对于默认构造函数的定义问题如何理解)
2.1.1 带有default constructor的成员类对象情况
如果一个class没有任何constructor,但是它内含一个成员类对象,而后者有默认构造函数,那么这个类的隐式构造函数就属于nontrivial类型。编译器需要为该类合成一个默认构造函数,但该合成操作只有在构造函数真正需要被调用时才发生,例如:
class Foo {
public:
Foo();
Foo(int);
};
class Bar {
public:
Foo foo; //含成员类对象,编译器通过构造函数初始化
char* str; //编译器不初始化,是程序员的责任
};
类Bar合成后的默认构造函数伪码如下:
inline Bar::Bar() {
foo.Foo::Foo();
} //注意没有char*的初始化
注意:如果类A内含一个或以上的成员类对象,那么类A的每一个构造函数必须调用每一个成员类的默认构造函数(按照其在类中的声明顺序)。编译器会扩张已存在的构造函数,在用户定义的代码前安插一些代码,先调用必要的默认构造函数。
2.1.2 带有default constructor的基类情况
如果一个没有任何构造函数的类派生自一个带有默认构造函数的基类,那么这个派生类的默认构造函数就属于nontrivial类型,它调用上一层基类的构造函数(根据声明的顺序),如:
class ZooAnimal {
public:
ZooAnimal() { cout << “ZooAnimal()” << endl; }
};
class Bear : public ZooAnimal {
public:
Bear() { cout << “Bear()” << endl; };
};
class Panda : public Bear { …… };
类Panda合成后的默认构造函数伪码如下:
inline Panda::Panda() {
ZooAnimal::ZooAnimal();
Bear::Bear();
}
2.1.3 带有一个虚函数的类情况
class ZooAnimal {
public:
ZooAnimal() { cout << “ZooAnimal()” << endl; }
virtual ~ZooAnimal() { …… }
};
2.1.4 带有一个抽象基类(virtual base class)的类情况
尽管抽象基类的实现方法根据编译器不同有很大差异,但共同点在于必须使virtual base class在其每一个派生类对象中的位置在执行期准备妥当。
class X { ……. };
class A : public virtual X { …… };
class B : public virtual X { …… };
class C : public A, public B { …… };
2.1.5 默认构造函数认识误区
1. 任何类如果没有定义默认构造函数,编译器会为之合成一个。(确切地说,此处所指的合成默认构造函数是nontrivial类型,但也可以认为编译器会合成,但是合成的默认构造函数是trivial类型,这符合C++标准的定义)
2. 编译器合成的默认构造函数会明确设定“类内每一个数据成员的默认值”。(即对非静态数据成员如整数,整数指针,整数数组都不会初始化)