Effective C++ 笔记12: 在 constructors 中对成员变量采取初始化而不要使用赋值
有时候我们会写这样的类:
template<class T>
class NamedPtr {
public:
NamedPtr(const string& initName, T *initPtr);
...
private:
string name;
T *ptr;
};
在 constructors 中要对变量进行初始化,这时可以有两种办法:
第一种是使用初始化列表,也就是这么写:
template<class T>
NamedPtr<T>::NamedPtr(const string& initName, T *initPtr )
: name(initName), ptr(initPtr)
{}
另一种是使用赋值,这么写:
template<class T>
NamedPtr<T>::NamedPtr(const string& initName, T *initPtr)
{
name = initName;
ptr = initPtr;
}
这时一般我们都应该选择第一种方法,即使用初始化列表。
1. 使用初始化列表的效率比较高
在我们新建一个类时,类的成员变量都会先被初始化,然后再调用这个类的 constructors ,如果我们使用赋值的方法,那么在 NamedPtr::NamedPtr 被调用之前,要先调用一个 string 的默认构造函数,把 name 构造出来,然后我们又在 NamedPtr::NamedPtr 中调用了一次 string 的赋值函数 operator =,所以总共调了两个 string 的成员函数。而如果我们使用初始化列表,那么在构造 name 时调用的就是 copy constructor,而在 NamedPtr::NamedPtr 中我们没有调用别的函数,所以总共调用了一个 string 的成员函数。当成员变量很大时,这种效率上的区别就会很明显了。
2. 使用赋值函数不能初始化 const 成员与引用型的成员
比如我们希望上面的 name 是一个 const 成员,那么就只能使用初始化列表来初始化,因为 const 成员是不能被赋值的。同样,引用型的成员也不能被赋值,因为对它的赋值事实上是对它所指向的对象的赋值。
但是有时候我们也会考虑使用赋值,如果成员变量很多,而且都是很小的,把它们都写在初始化列表里会很乱,而且如果有几个 constructor 就得写几遍,这样很容易造成混乱。这时就可以写一个函数,所赋值操作都放到这个函数里,然后在所有的 constructor 里都调用这个函数。如下:
class ManyDataMbrs {
public:
// default constructor
ManyDataMbrs();
// copy constructor
ManyDataMbrs(const ManyDataMbrs& x);
private:
int a, b, c, d, e, f, g, h;
double i, j, k, l, m;
void init(); // used to initialize data
// members
};
void ManyDataMbrs::init()
{
a = b = c = d = e = f = g = h = 1;
i = j = k = l = m = 0;
}
ManyDataMbrs::ManyDataMbrs()
{
init();
...
}
ManyDataMbrs::ManyDataMbrs(const ManyDataMbrs& x)
{
init();
...
}