C++ 默认构造函数
一直认为若程序员没有自己定义无参数的构造函数,那么编译器会自动生成默认构造函数,来进行对成员函数的初始化,但这种认为是有误的,不全面的.
默认的构造函数分为有用的和无用的,所谓无用的默认构造函数就是一个空函数、什么操作也不做,而有用的默认构造函数是可以初始化成员的函数。
对构造函数的需求也是分为两类:一类是编辑器需求,一类是程序的需求。
程序的需求:若程序需求构造函数时,就是要程序员自定义构造函数来显示的初始化类的数据成员。
编辑器的需求:编辑器的需求也分为两类:一类是无用的空的构造函数,一类是编辑器自己合成的有用的构造函数。
在用户没有自定义构造函数的情况下:一、由于编辑器的需求,编辑器会调用空的无用的默认构造函数。二、但在下列情况下:编辑器就一定会合有用的默认构造函数。
看下面的例子:
#include <iostream> using namespace std; class Foo { public: int val; Foo *pnext; }; void foo_bar() { // Oops: program needs bar's members zeroed out Foo bar; if ( bar.val || bar.pnext ) cout << "Print the content !" << endl; } int main() { foo_bar(); return 0; }
程序可以通过编译,但是显示:The variable 'bar' is being used without being initialized.
这就说明建立Foo bar; 这个对象编辑器并没有自动合成默认构造函数。
默认的构造函数也分为:有用的构造函数和无用的构造函数,所谓有用的构造函数就是构造函数会为我们的类做一些初始化的工作,而无用的构造函数对我们的类没有任何意义,我们常说的默认构造函数就是值有用的构造函数,英文为:nontrivial default constructor。
什么情况下编辑器才会自动合成有用的默认构造函数?(以下的默认构造函数均值有用的默认构造函数)
下面四种情况,编辑器才会背着用户自己合成默认构造函数:
一、带有“Default Constructor"的Member class Object.
如果一个类没有任何Constructor,但它内含一个带有Default Constructor的Member class Object。编辑器就会自动为此类合成一个默认的构造函数。
看下面的例子:
#include<iostream> using namespace std; class Foo { public: Foo(); Foo( int ); private: int val; }; Foo::Foo() { cout << "Call Foo::Foo() Constructor !"<< endl; val = 0; } Foo::Foo(int i) { cout << "Call Foo::Foo(int i) Constructor !"<< endl; val = i; } class Bar { public: Foo foo; char *str; }; void foo_bar() { Bar bar; // Bar::foo must be initialized here if (bar.str ) { cout << "Print the content !" << endl; } } int main() { foo_bar(); return 0; }
输出为:
Call Foo::Foo() Constructor !
Print the content !
类class Bar 没有自己定义的Constructor,而成员中的Foo foo; 却带有字定义的Constructor(Foo::Foo()),这种情况符合:"带有“Default Constructor"的Member class Object."
由输出结果可以看出:编辑器为class Bar合成了默认的构造函数,并调用了Foo的自定义构造函数。
那么编辑器为class Bar合成了默认的构造函数应该是什么样子呢?
inline Bar::Bar() { foo.Foo::Foo(); }
编辑器会自动合成如上的默认构造函数。
如果一个类自定义了Constructor,但它内含一个带有Default Constructor的Member class Object。编辑器就会自动扩充此类的自定义构造函数。
如果类Bar自定义了构造函数,那么编辑器会怎么做,如下代码:
class Bar { public: Bar(); Foo foo; char *str; }; Bar::Bar() { cout << "Call Bar::Bar() Constructor !"<< endl; str = 0; }
输出为:
Call Foo::Foo() Constructor !
Call Bar::Bar() Constructor !
看到输出结果仍然是调用了Foo的自定义的构造函数”Call Foo::Foo() Constructor !“。
那么这是如何做到的呢?
编辑器会扩充Bar自定义的构造函数,其结构如下:
Bar::Bar() { foo.Foo::Foo(); //这里是编辑器自动添加上去的 cout << "Call Bar::Bar() Constructor !"<< endl; str = 0; }
二、如果派生列的基类中有自定义的nontrivial default constructor,那么编辑器会为每一个派生类合成一个nontrivial default constructor,以调用基类自定义的nontrivial default constructor。
三、如果一个类里隐式的含有Virtual tabel(Vtbl)或者pointer member(vptr)
vtbl或vptr需要编辑器隐式的合成出来,那么编辑器就把合成动作放在了默认构造函数里,所以编辑器必需自己产生一个构造函数来完成这些动作。
所以你的类里只要含有virtual function,那么编辑器就会生成默认的构造函数。
四、如果一个类虚继承与其他类
理由和上面一样,虚基类也需要vtbl和vptr管理,那么这些管理就需要合成构造函数来实现管理,则需要生成默认的构造函数。
所以类的基类有虚类,那么编辑器就会合成默认的构造函数。