构造函数语意学
1,默认构造函数(default constructors)是在需要的时候被编译器产生出来。这里需要理解的是被谁需要,产生出来做什么事情。分析下面这段代码
9 class foo 10 { 11 public: 12 int m_nval; 13 foo *pnext; 14 }; 15 16 void foo_bar() 17 { 18 foo bar; 19 if (bar.m_nval 20 || bar.pnext != NULL) 21 { 22 // do something 23 cout<<"do something..."<<endl; 24 } 25 }
从上面的例子中,可以看出foo_bar()函数中定义了一个foo类对象bar,当这个对象bar初始化后的两个成员变量被赋值时输出一句话。上面这段代码编译器会产生出一个默认构造函数(default constructors)吗?答案是不会。原因在于一个是程序的需要,一个是编译器的需要。程序的需要是通过程序员来编写构造函数的,而编译器的需要是通过编译器默认的产生出来的。
只有4种情况,编译器才会产生默认构造函数,这种构造函数被视为是一种nontrivial default constructors(有用的默认构造函数)。这4种情况分别是:
1,“带有default constructor”的Member class object。
2,“带有default constructor”的base class
3,“带有virtual function”的class
4,“带有一个virtual base class”的class
下面分别介绍这几种情况
1,“带有default constructor”的Member class object,例子:
1 class Foo 2 { 3 public: 4 Foo(){}; 5 Foo(int nval){ m_nval = nval;}; 6 public: 7 int m_nval; 8 }; 9 10 class Bar 11 { 12 public: 13 Foo foo; // 带有default constructor”的Member class object 14 char *str; 15 }; 16 17 void foo_bar() 18 { 19 Bar _bar;//Bar::foo函数成员对象 必须在此处合成出来 20 if (_bar.str != NULL) 21 { 22 cout<<"do something..."<<endl; 23 } 24 }
上面这段代码,编译器为class Bar合成一个default constructor, 被合成出来的default constructor函数体内含有必要的代码能够调用class Foo的默认构造函数
Foo(){};来处理Bar中的Bar::foo,但是这个default constructor不产生任何的代码来初始化Bar::str成员函数,因为将Bar::foo初始化是编译器的责任,将Bar::str初始化则是程序员的责任。
被编译器后台合成出来的default constructor应该类似于:
class Bar { public: Foo foo; char *str; public: inline Bar(); }; inline Bar::Bar() { foo.Foo::Foo();//伪代码 }
再次强调,被编译器后来合成出来的构造函数只是用来满足编译器的需要,而不是程序的需要。
为了能够让上面程序正确的执行,字符指针*str也需要被初始化,这需要程序员提供一个构造函数来对这个指针进行初始化,初始化构造函数如下:
Bar::Bar() { str = "构造实例"; }
现在程序的需求满足了,但是编译器还需要初始化Bar::foo对象,由于默认构造函数已经明确的被定义出来了,编译已不能合成默认的构造函数,这个时候编译器会采取这种方式“如果class A 内含有一个或一个以上的member class object,那么class A的每一个constructor必须调用每一个member class object的default”。这时编译器会扩张已存在的构造函数,在其中安插一些代码,使得user code在被执行之前,先调用必要的default constructor.。扩张后的代码类似于:
Bar::Bar() { foo.Foo::Foo();//编译器扩张的代码,伪代码 str = "构造实例"; }
对于含有多个memeber class object。C++语言要求按照这些成员类对象声明的次序来调用各个的constructor。
2,“带有default constructor”的base class
类似的道理,如果一个构造函数的类派生自一个带有默认构造函数的类,那么这个派生类在创建对象的时候,编译器隐含的合成一个默认构造函数。原理同1类似。
3,“带有virtual function”的class
另有两种情况也需要编译器合成默认构造函数,1:class声明或者继承了一个虚函数,2.class派生自一个继承串链,其中有一个或者多个虚基类。
不管哪一种情况,由于没有人为的声明一个构造函数,那么编译器需要自己合成一个默认的构造函数。
4,“带有一个virtual base class”的class
上面四种情况,会使编译器必须为一个未 人为声明构造函数的类 去合成一个默认的构造函数,被合成出来的默认构造函数只能满足编译器自己的需要。对于没有存在这四种情况并且又没有人为的声明构造函数的类,他们的默认构造函数不会被合成出来。
常见的两种误解:
1,任何class如果没有定义构造函数,就会被合成出来一个
2,编译器合成出来的默认构造函数会明确的设定class内的每一个数据成员的默认值。
这两种想法都是都是错误的