Effective_C++:11、如果class内有动态分配内存,请为class声明一个拷贝构造函数和赋值运算符

11、如果class内有动态分配内存,请为class声明一个拷贝构造函数和赋值运算符

1、拷贝构造函数和赋值运算符

        创建一个class,若没有声明拷贝构造函数和赋值运算符,如同没有声明构造函数和析构函数一样,会有默认的拷贝构造函数和赋值运算符。
        拷贝构造函数用于程序中按值传递的地方,而赋值运算符仅用于两个已声明的对象。

2、有动态分配内存时,默认的拷贝构造函数和赋值运算符

class String {
public:
    String(const char *value);
    ~String();
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; }
        一个简陋的String class,其中没有拷贝构造函数和赋值运算符,故会调用默认的拷贝构造函数和赋值运算符。
String a = "Hello";
String b = "world";
b = a;//赋值运算符
        b = a;将调用默认的赋值运算符,即将a的成员一一赋值给b。a、b均为String对象,内部只含一个char指针,而默认的赋值运算符会将a的成员赋给b的成员,即令b的指针指向的地址等于a的指针指向的地址。也就是说,他们指向同一块内存。
        这里,至少有两个问题:1.b的指针原来指向的内存块并没有被释放,典型的内存泄漏。2.a、b的指针指向同一块内存,若删除某个指针,即销毁某个对象时,另外一个指针将没有定义,没有指向动态分配的内存。
void doNothing(String localString) {}
String s = "The Truth Is Out There";
doNothing(s);
        对于拷贝构造函数,如调用doNothing(),通过按值传递将String s传递给函数。此时,localString将使用默认的拷贝构造函数拷贝s,他将包含一个s对象的指针的副本,即他包含的指针与s的成员指针指向同一块内存。当函数调用结束,localString的作用域结束,localString调用析构函数,指针指向的内存被释放。即s包含的指针指向了已被localString删除的内存。
        对于已删除的内存,如果再次进行delete,其结果未定义,将是不确定的。

3、为class声明一个拷贝构造函数和赋值运算符

        凡是这些所谓的指针别名问题,解决之道是:若class含有指针,请撰写自己的拷贝构造函数和赋值运算符,将指针所指的数据结构也进行拷贝。或者,也可以实现某种引用计数策略,追踪有多少对象指向该数据结构。
        对于某些class,你确信他不会用的拷贝构造函数和赋值运算符,则将拷贝构造函数和赋值运算符声明为private,而无需实现他。
        除此,应记得上述,两次使用new时都搭配了[],即使有一处是单一对象。为使new搭配delete的形式,delete使用了[],故两次new均使用了[]。

猜你喜欢

转载自blog.csdn.net/l1dian11/article/details/80996525