:构造函数语意学之Default constructor的构造操作

Posted ccpang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:构造函数语意学之Default constructor的构造操作相关的知识,希望对你有一定的参考价值。

C++新手一般由两个常见的误解:

  • 如果任何class没有定义默认构造函数(default constructor),编译器就会合成一个来。
  • 编译器合成的的default constructor会显示的设定“class内每一个data member的默认值”

 


一、编译器在哪种情况下才会合成默认构造函数:

对于未声明构造函数的类,只有在以下四种情况下编译器才会为它们合成默认构造函数:

  • 类的成员有一个类对象(Member Class Object),且该成员含有默认构造函数(default Constructor)
  • 继承自带有默认构造函数(default constructor)的基类(Base class)
  • 带有虚函数(virtual function)的类
  • 继承自虚基类(virtual base class)的类

  对于以上四种情况,C++标准把合成的默认构造函数叫隐式的有意义默认构造函数(implicit nontrivial default constructors)。被合成的构造函数只能满足编译器(而非程序)的需要,它之所以能够完成任务,是借着调用成员对象或基类的默认构造函数(情况1/2),或是为每一个对象初始化其虚函数机制或虚基类机制(情况3/4)。

  至于没有存在上述四种情况,而又没有声明任何构造函数的类,那么它们拥有的是隐式无意义默认构造函数implicit trivial default constructors),实际上它们并不会被合成出来。

  在合成的默认构造函数中,只有basec class subobject(基类实例)、member class objects(成员类对象)会被初始化,其他的nonstatic data member(如整数、整数指针、整数数组等)都不会被初始化,初始化这些东西或许对程序而言是非常重要的,但是对于编译器来说则不是必要的。如果程序需要一个“把某指针设置为0”的默认构造函数,那么提供的它的人应该是程序员

 


 

二、情况一:“带有Default Constructor”的Member Class Object

  如果一个cass没有任何 constructor,但它内含一个 member object,而后者有default constructor,那么这个 class的 implicit default constructor就是“ nontrivial”,编译器需要为该 class合成出一个 default constructor。不过这个合成操作只有在constructor真正需要被调用时才会发生

  于是出现了一个有趣的问题:在C+各个不同的编译模块中(不同的文件),编译器如何避免合成出多个 default constructor(比如说一个是为A.C文件合成,另一个是为B.C文件合成)呢?       解决方法是把合成的 default constructor、constructor、 destructor、 assignment copy operator都以 inline方式完成。一个inline函数有静态链接( static linkage),不会被文件以外者看到。如果函数太复杂,不适合做成 inline,那就会合成出一个 explicit non-inline static实例。

 1 //编译器为 class Bar合成一个 default
 2 class Foo  public: Foo(), Foo( int ).. ;
 3 class Bar  public: Foo foo;  char*str; ;//译注:不是继承,是内含!
 4 
 5 void foo bar()
 6 (
 7     Bar bar;    //Bar::foo必须在此处初始化。
 8         //译注:Bar::foo是一个 member object,而其 class Foo
 9         //拥有 default constructor,符合本小节主题
10 if( str )   ...
11 
12                   

 

 

 

  被合成的 Bar default constructor内含必要的代码,能够调用 class Foo的 default  constructor来处理 member object Bar::foo,但它并不产生任何代码来初始化Bar:;str。是的,将Bar::foo初始化是编译器的责任,将Bar:str初始化则是程序员的责任。

  被合成的默认构造函数如下所示:

 

//Bar 的默认构造函数可能会被合成如下形式:
//为成员foo调用类Foo的默认构造函数
inline Bar::Bar()

    foo.Foo::Foo();

 

 

 

  不过合成的默认构造函数只是满足编译器的需要,为了能够让程序顺利执行,字符指针str也需要被初始化。假如我们用下面的构造函数为str提供了初始化:

//程序员定义的默认构造函数
Bar::Bar() str = 0;

 

 

 

那么问题就来了,既然程序员自己显示定义了默认构造函数,编译器就不会再定义第二个(温饱已经解决,就不能再吃救急粮),那么编译器是怎么做的呢?

通过扩张程序员自己定义的构造函数,在其之前先调用成员类的默认构造函数,如果有多个类成员对象都要求constructor初始化操作,则将按成员对象在类内的声明顺序来调用各个成员类的构造函数,如下所示:

 

 1 class Dopey  public : Dopey();...;
 2 class Sneezy public : Sneezy (int); Sneezy();...;
 3 class Bashful public : Bashful(); ...;
 4 
 5 //class Snow_White内含上述三个类:
 6 class Snow_White
 7 
 8 public:
 9     Dopey dopey; 
10     Sneezy sneezy;
11     Bashful bashful;
12 private:
13     int mumble;
14 
15 
16 //如果程序员没有定义默认构造函数,则编译器会自动合成 implicit nontrivial default constructor,其调用顺序按上述声明;
17 //如果程序员定义了如下的构造函数:
18 
19 Snow_White::Snow_White() : sneezy(1024)
20 
21     mumble = 2048;   
22 
23 
24 //那么编译器会将上述构造函数自动扩展成如下所示:
25 Snow_White::Snow_White() : sneezy(1024)
26 
27   //隐式加入member class object 的default  constructors
28    dopey.Dopey::Dopye();
29    sneezy.Sneezy::Sneezy(1024) ;
30    bashful.Bashful::Bashful();
31    
32     mumble = 2048;   
33 

 

以上是关于:构造函数语意学之Default constructor的构造操作的主要内容,如果未能解决你的问题,请参考以下文章

Java继承和构造函数

CultureInfo.CreateSpecificCulture() 和类的构造函数之间的区别?

容器小结

C++构造函数的default和delete

对于默认构造函数和析构函数,“=default”与“”有何不同?

每日一学之Java开发技巧正则表达式