Effective C++ 学习笔记:为含指针变量的类声明一个拷贝构造函数和一个赋值操作符

时间:2020-12-20 19:29:06

C++编译器会为每个类自动生成一个默认的构造函数、析构函数、赋值函数、拷贝构造函数,这当然是在你没有为你的类声明这些函数的时候。这些默认的功能函数在为你提供方便的时候,也会给你带来麻烦。

例如:

class string {
public:
  string(const char *value);
  ~string();

  ...                           // 没有拷贝构造函数和operator=

private:
  char *data;
};

string::string(const char *value)
{
  if (value) {
    data = new char[strlen(value) + 1];
    strcpy(data, value);
  }
  else {
    data = new char[1];
    *data = '/0';
  }
}

inline string::~string() { delete [] data; }           //注意:new 和delete 要采用相同的形式。

如果有string的两个对象,

string a("hello");
string b("world");

当b=a时,因为你自己没为类定义那些函数,所以C++编译器会提供默认的赋值函数,这个缺省的赋值操作符会执行从a的成员到b的成员的逐个成员的赋值操作,对指针(a.data和b.data) 来说就是逐位拷贝。这种情况下至少有两个问题。

第一,b曾指向的内存永远不会被删除,因而会永远丢失。这是产生内存泄漏的典型例子。

第二,现在a和b包含的指针指向同一个字符串,那么只要其中一个离开了它的生存空间,其析构函数就会删除掉另一个指针还指向的那块内存,重复析构的问题。

下面的语句:

string a("hello");                // 定义并构造 a

{                                        // 开一个新的生存空间
  string b("world");            // 定义并构造 b

  ...

  b = a;                              // 执行 operator=,    调用默认赋值函数
                                         // 丢失b的内存,造成内存泄露。

}                                        // 离开生存空间, 调用
                                         // b的析构函数

string c = a;                       // c.data 的值不能确定!  调用默认的拷贝构造函数 
                                         // 但是a.data 已被删除,无法进行拷贝构造。

 

当这类对象进行函数参数按值传递时,形参会按照缺省的拷贝构造函数进行初始化,形参拥有一个指向该对象指针的一个拷贝,当函数结束时,形参会调用析构函数,该对象的指针也被销毁。

 

牢记:只要类里有指针变量就得自己写拷贝构造函数和赋值函数,但是你确定用不着这些函数时,可以把这些函数做private声明而不去实现它,这就防止了会有人去调用它们,也防止了编译器去生成它们。