目录
1. 问题引入
2. 4种implicitly声明的default constructor
1. 问题引入
“default constructors......在需要的时候被编译产生出来”。关键词是“在需要的时候”,被谁需要,做什么事情?看看下面的代码,然后梳理下思路。
class Foo
{
public:
int val;
Foo *pnext;
};
void foo_bar()
{
//程序要求bar's members都被清空为0
Foo bar;
if(bar.val || bar.pnext)
// ... do something
//...
}
在上面的代码中,正确的程序语意是要求Foo有一个default constructor,可以将它的两个members初始化为0。然而“在需要的时候”?答案是No,其中的差别是一个被程序需要,一个被编译器需要。程序如果需要,那是程序员的责任,本例中承担的责任是设计class Foo的人。是的,上述的代码不会合成一个default constructor。
那么什么时候合成一个default constructor?C++ Stand[ISO-C++95]的Section2.1这么说:对于class X,如果没有任何的user-declared constructor,那么会有一个default constructor 被implicitly(隐式)声明出来......一个被implicitly声明出来的default constructor 将是一个trivial(没啥用的)constructor。
下面分4种情况讨论default constructor 被implicitly声明出来。
2. 4种implicitly声明的default constructor
2.1)带有default constructor的member class object
如果一个class没有任何的constructor,但是它内含一个member object,而后者有一个default constructor,那么这个class 的implicit default constructor就是nontrivial,编译器需要为该class合成一个default constructor。举个例子,我们看看如下的代码,在这个代码中Bar会合成一个default constructor。
class Foo{
public:
Foo();
Foo(int);
};
class Bar{
public:
Foo foo;
char *str;
};
void foo_bar(){
Bar bar;//Bar::foo必须在此处初始化。
//Bar::foo是一个member object,而其
//class Foo拥有default constructor,符合要求
if(bar.str){}....
}
扩张后的代码可能是这样子。
//扩张后的default constructor
//C++伪码
Bar::Bar()
{
foo.Foo::Foo();//附加上的compiler code
str = 0;//explicit user code
}
2.2)带有default constructor的Base Class
如果一个Base Class具有default constructor,而这个基类的derived class没有default constructor,此时derived class的default constructor会被视作nontrivial,因此需要被合成出来。他将调用上一层base class的default constructor(根据他们声明的顺序)。
2.3)“带有一个virtual function”的Class
另有两种情况,也需要合成default constructor:
- Class 声明(或继承)一个virtual function。
- Class派生一个继承串链,其中有一个或者更多的virtual base classes。
以下的程序为例子,每个class object中,一个额外的pointer member(也就是vptr)会被编译器合成出来,内含class 的virtual function地址。Widegt要合成一个default constructor,Bell也要合成一个default constructor,Whistle也要合成一个default constructor。
class Widget{
public:
virtual void flip() = 0;
};
void flip(const Widget &widget)
{
widget.flip();
}
class Bell:public Widget{
public:
void flip(){cout<<"Bell"<<endl;}
};
class Whistle:public Widget{
public:
void flip(){cout<<"Whistle"<<endl;}
};
void foo()
{
Bell b;
Whistle w;
flip(b);
flip(w);
}
2.4)“带有一个virtual base class”的Class
virtual base class的实现法在不同的编译器之间存在非常大的差异,然而每一种实现法的共同点在于必须使得virtual base class在其每一个derived class object中的位置,能够在执行期准备妥当。例如如下的代码,代码中A,B,C均需要合成一个default constructor,并在里面安插那些“允许每一个virtual base class的执行期存取操作”的代码。
class X {public: int i;};
class A:public virtual X{public: int j;};
class B:public virtual X{public: double d;};
class C:public A,public B{public: int k;};