default constructor 会在需要的时候被编译器产生出来,这个需要怎么理解呢?
我们可以看看下面的代码:
class link{public:int val; link *next;}
void text1(){
link tmp;
if(tmp.val||tmp.next){
//
}
}
在上面的代码中,我们看到,在text1函数里的判断语句是需要tmp里的val或者next有一个具体的值的(要不然怎么判断呢?)而这个情况就属于程序需要,而满足这个这个需要就应该是程序员的事情。但是在看深度探索c++模型的时候我了解到,不管对于程序需要还是编译器需要,都会构造出一个default constructor,而向上面的那段代码,编译器也会生成一个default constructor,但是基本上没什么用,书上叫它(trivial constructor).
当然,有了trivial constructor,也肯定有nontrivial constructor,那么编译器什么时候会合成一个nontrivial constructor 呢?
1.带有默认构造函数的对象成员
首先我们看以下的代码(下列代码来自书上的例子)
class foo{public:foo(),foo(int)...};
class bar{public:foo a;char *str;};
void foo_bar(){
bar tmp;//在这一步的时候,foo的默认构造函数会被调用
if(str){};
}
按找之前的说法,这里编译器会生成一个default constructor (for bar),而按照上面的代码来看,这个constructor 得包含能够调用foo::foo()的代码,但是这里要注意,编译器并不会对str进行初始化。编译器生成的default constructor 可能像下面的一样
//c++伪码
inline bar::bar(){
a.foo::foo();
}
而我们加上程序员写的构造函数后,它会变成这样
bar::bar(){str=0;}
//c++伪码
bar::bar(){
a.foo::foo();
str=0;
}
可以看到,bar 在生成default constructor 的时候,会先去调用含有default constructor 的member object里的default constructor 函数,然后再去调用用户自定义的constructor。
那么看到这里,我们肯定会有疑问,如果含有多个member object 的话,default constructor 将会如何构造,
这里的话,在书上可以看到,c++语言要求以member object 在class的声明顺序来调用各个constructor,然后再去调用用户自定义的constructor。具体可以看下面的代码
class Dopey{public:Dopey();};
class Sneezy{public:Sneezy(int );Sneezy();...};
class Bashful{public:Bashful();...};
class show_white{
public:
Sneezy one;
Dopey two;
Bashful three;
private:
int count;
};
show_white::show_white():sneezy(2048){
count=2048;
}
constructor 会被扩张为
//c++伪码
show_white::show_white()::one(2048){
one.Sneezy::Sneezy(2048);
two.Dopey::Dopey();
three.Bashful.Bashful();
count=2048;
}
这里着重理解一下扩张
2.该类是继承而来,且父类自带有defult constuctor
class A{
private:
int val1;
int val2;
public:
A(){
///初始化内容
}
};
class B:public A{
};
在这里,B会执行A的constructor
3.该函数里面有虚函数
class A{
public:
void virtual foo(){};
};
在这里,default constructor会构造出vptr(用于指向虚表),和虚表,
所以它是有用的
4.带有一个virtual base class 的class
class X{public:int i;};
class A:public virtual X{public:int j;};
class B:public virtual X{public:int k;};
class C:public A,public B{public:double m;};
在这里,编译器会给派生类生成一个指针,指向virtual base class ,
可以看下面的代码来理解一下这个
void foo(const A* pa){pa->i=1024}//这个是错的,因为无法在编译器里面决定pa->X::i的偏移位置
所以想出的办法是在每一个派生类里面设置指向virtual base class 的指针,
//如下
void foo(const A* pa)(pa->_vbcx->i=1024;)