对于class X,如果没有任何user-declared constructor, 那么会有一个default constructor被隐式(implicit)声明出来,但是这个default construct是无用的。关键词explicit被导入,就是给我们提供一种方法,他们能够制止“单一参数的construct”被当成一个conversion运算符。
class Foo { public: int val; Foo *pnext; };
default construct在编译器需要的时候自动产生一个,而在程序需要的时候编译器不会产生,因为这是程序员的责任。
void foo_bar() { Foo bar; if(bar.val||bar.pnext) //do something //... }
上述代码并不会自动生成default construct,所以要想使程序正常工作,必须显示的声明一个default construct。
Global object的内存保证会在程序启动时候被请清为0,。local object配置于程序的堆栈中,heap object配置于自由空间中,都不一定会被清为0,它的内容是上次使用后的。
1.“带有Default Construct”的Member Class Object
如果一个class没有任何construct,但是它含有一个member object,而后者有default construct,那么这个class的implicit default construct是有用的,编译器会合成一个default construct,只不过这个construct真正被调用的时候才会发生。但是c++有不同的编译模块,编译器为了避免合成多个default construct,把合成的default construct,copy construct,destructor,assignment copy operator都以inline的方式完成。
class Foo {public:Foo(), Foo(int) }; class Bar {public: Foo foo;char *str;}//内含 void fun() { Bar bar;//此时会合成默认构造函数,会调用Bar的默认构造函数 } //编译器在Bar中插入代码,合成default construct inline Bar::Bar() { foo.Foo::Foo(); }; //但不会初始化str,需要程序员来进行初始化;
编译器合成的default construct只是满足编译器的需要,而不会满足程序的需要,所以bar.str并不会被初始化,要想初始化此成员,要手工定义default construct。
但是如果程序员自己定义构造函数,如下,构造函数被现实定义,那么编译器就不会再合成第二个default construct。
Bar::Bar() { str=0; }
那么为了编译器的需要,编译器会扩充已存在的construct(如下)。也就是:如果class A内含有一个或一个以上的member class object,那么class A的每个construct都必须调用每个member classes的default construct。在user conde被调用之前,先调用default construct。
Bar::Bar() { foo.Foo::Foo();//附加compiler code str=0;//explicit user code }
如果有多个class member objects都要求contructor初始化操作。C++语言要求以“member objects在class中的声明顺序“来调用各个constructors。这一点由编译器完成,它为每个constructor安插程序代码,以”member声明顺序“调用每一个member所关联的default constructors。这些代码将被安插在explicit user code之前。
class Dopey {public: Dopey();...}; class Sneezy {public: Sneezy(int); Sneezy();...}; class Bashful {public: Bashful();...}; //以及一个class Snow_White: class Snow_White { public: Dopey dopey; Sneezy sneezy; Bashful bashful; private: int mumble; }; //如果Snow_White没有定义default constructor,就会有一个nontrivial constructor被合成出来, //依序调用Dopey、Sneezy、Bashful的default constructors。然而如果Snow_White定义了下面这样 //的default constructor: //程序员所写的default constructor Snow_White::Snow_White(): sneezy(1024) { mumble = 2048; } //编译器扩张后的default constructor Snow_White::Snow_White():sneezy(1024) { //插入member class object //调用其constructor dopey.Dopey::Dopey(); sneezy.Sneezy::Sneezy(1024); bashful.Bashful::Bashful(); //expilict user code mumble = 2048; }
总结:
2."带有Default Constructor“的Base Class
类似的道理,如果没有任何constructors的class派生自一个”带有default constructor“的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。它将调用上一层base classes的default constructor(如果是多继承,根据他们声明的顺序)。对一个后继的class而言,这个合成的constctor和一个”被显式提供的default constructor“没有什么差异。
如果设计者提供多个constructors,但其中都没有default constructor呢?编译器会扩张现有的每一个constructors,将“用以调用所有必要之default constructors”的程序代码加进去。它不会合成一个新的default constructor,因为其他“由user所提供的constructors”存在的缘故。如果同时亦存在着“带有default constructor”的member class objects,那些default constructor也会被调用--在所有base class constructor之后。
3.“带有一个virtual Function”Class
另有两种情况,也需要合成出default constructor:
假设基类中还有虚函数,派生类继承基类时编译期间会发生两步扩张活动:
编译器会为每一个派生类的vptr设定初始值,放置适当的virtual table地址,对于class定义的每个construct,编译器都会安插一些代码做这些事,对于未声明construct的class,编译器会合成construct来做这些事。
4.“带有一个virtual Base Class”的Class
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; }; void foo(const A* pa) { pa->i=1024; } int main() { foo(new A); foo(new C); }
编译器无法固定住foo中“由pa而存取的X::i”的实际偏移位置,因为pa的真正类型可以改变,编译器必须改变“执行存取操作”的那些代码。使得X::i延迟到执行期才决定下来。cfront的做法是在derived class object的每一个virtual base classes中安插一个指针完成。所有经由reference或pointer来存取的一个virtual base class的操作通过相关的指针来完成。
//可能编译器转变的操作 void foo(const A* pa) { pa->_vbcX->i=1024;//_vbcX表示编译器所产生的指针,指向virtual base class X,在class object的构造期被完成 }
对于class 所定义的每个construct,编译器会安插一些“允许每个virtual base class的执行期存取的操作”的代码,如果class没有声明任何constructors,编译器必须为它合成一个default constror。
总结,有4种情况,会造成“编译器必须为未声明constructor 的classes合成default constructor”。它们分别是:
C++Standard把那些合成物称为implicit nontrivial default constructors。被合成出来的constructos只能满足编译器(而非程序)的要求。它之所以完成任务,是借着“调用member object或base object的default constructor”或是“为每一个object初始化其virtual function机制或virtual base class机制”而完成的。至于没有存在那4种情况而又没有声明任何constructor的classes,我们说它们拥有的是implicit trivial default constructors,它们实际上并不会被合成出来。
在合成default constructor中,只有base class constructor和member class objects会被初始化。所有其他的nonstatic data member(如整数,整数指针、整数数组等等)都不会被初始化。这些初始化操作对程序而言或许有需要,但对编译器则非必要。如果一程序需要一个“把某指针设为0”的default constructor,那么提供它的人应该是程序员。
C++新手一般有两个常见的误解:
如你所见没一个是真的。
原文:https://www.cnblogs.com/tianzeng/p/12099034.html