8.6.2 默认复制构造函数带来的问题

    默认的复制构造函数并非万金油,在一些情况下,必须由程序员显式定义默认复制构造函数,先来看一段错误的代码示例,如代码8.10所示。

    代码8.10 默认的复制构造函数带来的问题ProblemOfCopyConstructor


    <—————————————文件名:computer.h————————————————> 01 #include<iostream> 02 using namespace std; 03 class computer 04 { 05 private: 06 char*brand; 07 float price; 08 public: 09 computer(const char*sz,float p) 10 { 11 brand=new char[strlen(sz)+1];//构造函数中为brand指针动态分配内存 12 strcpy(brand,sz); 13 price=p; 14 } 15 ~computer() 16 { 17 delete[]brand;//析构函数中释放申请到的动态内存 18 cout<<"清理现场"<<endl; 19 } 20 void print() 21 { 22 cout<<"品牌:"<<brand<<endl; 23 cout<<"价格:"<<price<<endl; 24 } 25 }; <—————————————文件名:example810.cpp——————————————> 26 #include"computer.h" 27 int main() 28 { 29 computer comp1("Dell",7000);//声明computer类对象comp并初始化 30 comp1. print(); 31 computer comp2(comp1);//调用默认的复制构造函数 32 comp2. print(); 33 return 0; 34 }

    【代码解析】编译链接没有错误,输出时却出现了如图8.3所示的错误提示框。

    按默认复制构造函数的语义,代码第31行语句“computer comp2(comp1);”等价于:


    comp2.brand=comp1.brand; comp2.price=comp1.price;

    8.6.2 默认复制构造函数带来的问题 - 图1

    图 8.3 默认的构造函数带来的问题

    其中,后一句“comp2.price=comp1.price;”是没有问题的,但“comp2.brand=comp1.brand”却给程序带来了致命的问题,经过赋值操作后,两个对象中的指针指向的是同一片动态内存,当comp1和comp2撤销时,其释放函数都要释放同一块动态内存,可是,两个对象撤销有先有后,一旦一个对象被撤销,另一个对象的brand指针便是“野指针”,使用该指针再次释放同一块动态内存会引发内存错误。不仅仅是重复释放内存的问题,默认复制构造函数有可能给程序带来不易发觉的错误,试分析下述代码。


    #include"computer.h" int main() { computer comp1("Dell",7000); if(true) { computer comp2(comp1); comp2.print(); } comp1.print();//comp1.brand指向的动态内存此时已经被释放 return 0; }

    由于comp2是在if结构中定义的局部对象,因此,在if结构退出时,comp2被撤销,系统自动调用其析构函数,释放了comp2.brand所指向的动态内存,由于comp2.brand和comp1.brand的值相同,此时comp1.brand已无所指,成了“野指针”,此时对该指针的读写操作都会引发无法预料的错误。