C++构造函数深度学习

2020-01-06 15:44:22丽君

本文针对C++构造函数进行深度探究,供大家参考,具体内容如下

1、引子: 
以下代码中的输出语句输出0吗,为什么?


struct Test
{
  int _a;
  Test(int a) : _a(a) {}
  Test()
  {
    Test(0);

  }

};
Test obj;
cout << obj._a << endl; 

输出为:-858993460

2、剖析
上面代码的输出为一个垃圾值,也就是说obj调用构造函数并没有对成员进行初始化工作,虽然默认无参构造Test()内部调用了Test(int a),但从结果看,初始化工作并不成功。这是为什么呢? 

在执行构造函数时,Test()并不会调用"this"对象(即obj对象)的Test::Test(int a),而是会用Test::Test(int a)来创建一个新的临时实例对象,然后当这条语句执行完后,这个新的临时对象马上就会被销毁。这样一来,"this"对象就没有被初始化,成员_a就是垃圾值,以后使用"this"对象就有可能产生一些问题。

3.重点:构造函数互相调用
 分析完这个题目之后,我们会想到另一个问题。也是我们今天重点关注的问题:


class Test
{ 
  int _a; 
  int _b; 
  int _c; 

public:

  Test(int a, int b) : _a(a), _b(b),_c(0) {} 

  Test(int a, int b, int c); 

}; 

如果我们C++类中有两个构造函数,分别为Test(int a, int b)和Test(int a, int b, int c)。如果我们的构造函数Test(int a, int b, int c)要完成所有成员(a,b,c)的赋值初始化工作,可以这样写:


Test::Test(int a, int b, int c)  

  : _a(a)

  , _b(b)

  , _c(c)

{ 

} 

但是,这样写又重复了构造函数Test(int a, int b)的工作,类成员少的情况下还好,如果成员非常多,重复写的话代码量过大,而且代码可读性降低了。然而我们可以看到构造函数Test(int a, int b)已经完成了成员a和成员b的赋值初始化工作,为了减少代码量,就想着让3个参数的构造函数调用2个参数的构造函数,然后在执行一些自己的代码,这就如同派生类先调用基类的同名函数,再执行自己特有的代码。但是这种机制如何实现呢? 

之前我们得出过结论:构造函数调用另一个构造函数并不能完成当前对象的初始化工作,只是初始化了临时对象。下面我们就进入本文的核心问题:如何在构造函数中调用本类的另一个构造函数来初始化当前对象?

方法一:使用placement new技术,在3个参数中显式调用2个参数的构造函数。
3参数构造函数可以这样实现:


Test::Test(int a, int b, int c)  
{ 
  new (this) Test(a, b); 
  ... 

} 

构造函数分为2个执行阶段:一是在初始化列表的初始化阶段,二是在构造函数体内的赋值阶段。上述方法是在第二个阶段调用2个参数的构造函数。