现在的位置: 首页 > 综合 > 正文

C++的初始化列表的使用

2014年01月17日 ⁄ 综合 ⁄ 共 2907字 ⁄ 字号 评论关闭

我的问题是关于初始化 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 会更加自然的出现.有两种方法避免它,一个 是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按 照它们声明的顺序罗列这些成员.这将有助于消除混淆.

 

示例代码片段,摘自live555:liveMedia/Media.hh

抱歉!评论已关闭.