成员初始化列表与构造函数体中的区别详细解析

 更新时间:2020年4月25日 17:44  点击:1971

论坛中回答一个别人问题

C++ Primer中在讲构造函数初始化列表的时候有这么一段话:
无论是在构造函数初始化列表中初始化成员,还是在构造函数体中对它们赋值,最终结果是相同的。不同之处在于,使用构造函数初始化列表的版本初始化数据成员,没有定义初始化列表的构造函数版本在构造函数体中对数据成员赋值。

请问这里的初始化数据成员与对数据成员赋值的含义是什么?有什么区别?

我知道在数据成员有默认构造函数时是有不同的,但对其他类型的成员呢?其他类型成员的初始化和赋值有区别吗?
=========================================================================
是这个意思:
首先把数据成员按类型分类
1。内置数据类型,复合类型(指针,引用)
2。用户定义类型(类类型)

分情况说明:
对于类型1,在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的
对于类型2,结果上相同,但是性能上存在很大的差别

因为类类型的数据成员对象在进入函数体是已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,这是调用一个构造函数,在进入函数体之后,进行的是 对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)

举个例说明
class A;
class B
{public:
B(){a = 3;}
private:
A a;
}

class A
{public:
A(){}
A(int){value = 3;}
int value;
}

像上面,我们使a对象的value为3,调用一个A的构造函数+一个默认拷贝赋值符,才达到目的
B::B():a(3){}
像这样,只调用了一个构造函数就达到了所需的对象啦,所以性能好的

转载他人一篇

我的问题是关于初始化C++类成员的。我见过许多这样的代码(包括在你的栏目中也见到过):

复制代码 代码如下:

CSomeClass::CSomeClass()

{

    x=0;

    y=1;

}


而在别的什么地方则写成下面的样子:
复制代码 代码如下:

CSomeClass::CSomeClass() : x(0), y(1)

{

}


我的一些程序员朋友说第二种方法比较好,但他们都不知道为什么是这样。你能告诉我这两种类成员初始化方法的区别吗?

回答

从技术上说,你的程序员朋友是对的,但是在大多数情况下,两者实际上没有区别。有两个原因使得我们选择第二种语法,它被称为成员初始化列表:一个原因是必须的,另一个只是出于效率考虑。

让我们先看一下第一个原因——必要性。设想你有一个类成员,它本身是一个类或者结构,而且只有一个带一个参数的构造函数。

复制代码 代码如下:

class CMember {

public:

    CMember(int x) { ... }

};


因为Cmember有一个显式声明的构造函数,编译器不产生一个缺省构造函数(不带参数),所以没有一个整数就无法创建Cmember的一个实例。

CMember* pm = new CMember;        // Error!!
CMember* pm = new CMember(2);     // OK

如果Cmember是另一个类的成员,你怎样初始化它呢?你必须使用成员初始化列表。

复制代码 代码如下:

class CMyClass {

    CMember m_member;

public:

    CMyClass();

};

//必须使用成员初始化列表

CMyClass::CMyClass() : m_member(2)

{

•••

}


没有其它办法将参数传递给m_member,如果成员是一个常量对象或者引用也是一样。根据C++的规则,常量对象和引用不能被赋值,它们只能被初始化。

第二个原因是出于效率考虑,当成员类具有一个缺省的构造函数和一个赋值操作符时。MFC的Cstring提供了一个完美的例子。假定你有一个类CmyClass具有一个Cstring类型的成员m_str,你想把它初始化为"yada yada."。你有两种选择:

复制代码 代码如下:

CMyClass::CMyClass() {

    // 使用赋值操作符

    // CString::operator=(LPCTSTR);

    m_str = _T("yada yada");

}

//使用类成员列表

// and constructor CString::CString(LPCTSTR)

CMyClass::CMyClass() : m_str(_T("yada yada"))

{

}


在 它们之间有什么不同吗?是的。编译器总是确保所有成员对象在构造函数体执行之前初始化,因此在第一个例子中编译的代码将调用CString:: Cstring来初始化m_str,这在控制到达赋值语句前完成。在第二个例子中编译器产生一个对CString:: CString(LPCTSTR)的调用并将"yada yada"传递给这个函数。结果是在第一个例子中调用了两个Cstring函数(构造函数和赋值操作符),而在第二个例子中只调用了一个函数。在 Cstring的例子里这是无所谓的,因为缺省构造函数是内联的,Cstring只是在需要时为字符串分配内存(即,当你实际赋值时)。但是,一般而言, 重复的函数调用是浪费资源的,尤其是当构造函数和赋值操作符分配内存的时候。在一些大的类里面,你可能拥有一个构造函数和一个赋值操作符都要调用同一个负 责分配大量内存空间的Init函数。在这种情况下,你必须使用初始化列表,以避免不要的分配两次内存。在内部类型如ints或者longs或者其它没有构 造函数的类型下,在初始化列表和在构造函数体内赋值这两种方法没有性能上的差别。不管用那一种方法,都只会有一次赋值发生。有些程序员说你应该总是用初始 化列表以保持良好习惯,但我从没有发现根据需要在这两种方法之间转换有什么困难。在编程风格上,我倾向于在主体中使用赋值,因为有更多的空间用来格式化和 添加注释,你可以写出这样的语句:x=y=z=0;

或者memset(this,0,sizeof(this));

注意第二个片断绝对是非面向对象的。

当我考虑初始化列表的问题时,有一个奇怪的特性我应该警告你,它是关于C++初始化类成员的,它们是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

复制代码 代码如下:

class CMyClass {

    CMyClass(int x, int y);

    int m_x;

    int m_y;

};

CMyClass::CMyClass(int i) : m_y(i), m_x(m_y)

{

}


你可能以为上面的代码将会首先做m_y=I,然后做m_x=m_y,最后它们有相同的值。但是编译器先初始化m_x,然后是m_y,,因为它们是按这样的顺 序声明的。结果是m_x将有一个不可预测的值。我的例子设计来说明这一点,然而这种bug会更加自然的出现。有两种方法避免它,一个是总是按照你希望它们 被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

[!--infotagslink--]

相关文章

  • C#中构造函数和析构函数用法实例详解

    这篇文章主要介绍了C#中构造函数和析构函数用法,结合实例形式详细分析了C#中构造函数与析构函数的原理、定义、使用方法与相关注意事项,需要的朋友可以参考下...2020-06-25
  • JavaScript精炼之构造函数 Constructor及Constructor属性详解

    除了创建对象,构造函数(constructor) 还做了另一件有用的事情―自动为创建的新对象设置了原型对象(prototype object) 。原型对象存放于 ConstructorFunction.prototype 属性中。例如,我们重写之前例子,使用构造函数创建...2015-11-08
  • 浅谈js构造函数的方法与原型prototype

    下面小编就为大家带来一篇浅谈js构造函数的方法与原型prototype。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-07-06
  • ASP.NET私有构造函数用法分析

    这篇文章主要介绍了ASP.NET私有构造函数用法,较为详细的分析了ASP.NET中私有构造函数的特性及具体用法,需要的朋友可以参考下...2021-09-22
  • C#中私有构造函数的特点和用途实例解析

    这篇文章主要介绍了C#中私有构造函数的特点和用途,需要的朋友可以参考下...2020-06-25
  • C++构造函数和析构函数的使用与讲解

    今天小编就为大家分享一篇关于C++构造函数和析构函数的使用与讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-04-25
  • C++聚合关系类的构造函数的调用顺序详解

    下面小编就为大家带来一篇C++聚合关系类的构造函数的调用顺序详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考,一起跟随小编过来看看吧...2020-04-25
  • 简单介绍php构造函数用法

    构造函数意思就是在类执行时通过构造函数作为入口进行操作了,我们下面来看一篇关于php构造函数用法吧。 构造函数 和 析构函数 构造函数 void __construct ([ mi...2016-11-25
  • C++中复制构造函数和重载赋值操作符总结

    这篇文章主要介绍了C++中复制构造函数和重载赋值操作符总结,本文对复制构造函数和重载赋值操作符的定义、调用时机、实现要点、细节等做了总结,需要的朋友可以参考下...2020-04-25
  • C++中构造函数的参数缺省的详解

    这篇文章主要介绍了C++中构造函数的参数缺省的详解的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下...2020-04-25
  • C++中构造函数重载

    这篇文章主要介绍了C++中构造函数重载的相关资料,十分的详细,需要的朋友可以参考下...2020-04-25
  • C#中派生类调用基类构造函数用法分析

    这篇文章主要介绍了C#中派生类调用基类构造函数用法,实例分析了派生类调用基类构造函数的技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • 浅析C#静态类,静态构造函数,静态变量

    这篇文章主要介绍了浅析C#静态类,静态构造函数,静态变量 的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • php中拷贝构造函数、赋值运算符重载详解

    本文章详细的介绍了关于php中拷贝构造函数、赋值运算符重载详解,有需要了解的同学可参考一下下哦。 对象的赋值与复制: 赋值:通过“ = ”运算符重载 ...2016-11-25
  • 详解C++中如何将构造函数或析构函数的访问权限定为private

    这篇文章主要介绍了详解C++中如何将构造函数或析构函数的访问权限定为private的方法,文中还解释了构造函数与虚函数的区别,需要的朋友可以参考下...2020-04-25
  • C++中的构造函数与析造函数详解

    这篇文章主要介绍了C++中的构造函数与析造函数详解的相关资料,需要的朋友可以参考下...2020-04-25
  • C++ 赋值构造函数注意点介绍

    下面小编就为大家带来一篇C++ 赋值构造函数注意点介绍。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
  • C++:构造函数,析构函数详解

    今天小编就为大家分享一篇关于C++构造函数和析构函数的文章,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2021-09-01
  • C++ 中构造函数的实例详解

    这篇文章主要介绍了C++ 中构造函数的实例详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下...2020-04-25
  • 关于C++复制构造函数的实现讲解

    今天小编就为大家分享一篇关于关于C++复制构造函数的实现讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-04-25