menu

hjk41的日志

Avatar

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();
  ...
}

评论已关闭