• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C++构造函数深度探究

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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个参数的构造函数。

placement new是operator new的一个重载版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new是不行的。也就是说placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。placement new技术的形式是 new(void *p) Type(...),表示在p所指的内存区域调用Type构造函数,该过程没有内存请求。

这个方法本质就是在对象地址处,调用2个参数的构造函数重新生成一个新的对象然后覆盖该对象。这个实现方法有投机取巧的嫌疑。 

 

方法二:使用C++11新特性——委托构造函数(Delegating constructors)。可以在构造函数初始化列表直接调用,类似于调用基类构造函数。

Test::Test(int a, int b, int c) : Test(a, b)  
{  
    ...  
}  

上述说了构造函数有2个执行阶段,该方法是在第一个阶段进行的,更加方便。但是注意不能在Test(a, b)后面在接_c(c)了,因为调用2个参数的构造函数之后,就相当于该对象已经初始化完成了,不能在初始化列表放入其他成员的初始化形式。只能放在构造函数体中的赋值阶段。该方法目前只能用在VS2013中。

这个方法利用了C++11标准中的新特性——委托构造函数(Delegating constructors)。目前只能再VS2013及以上的版本使用,这个方法局限性很大,不过确实很方便。 

 

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C++调用HNSW实现图像配准发布时间:2022-07-13
下一篇:
C++什么时候调用析构函数发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap