拷贝构造函数、拷贝运算符、析构函数
定义行为像值的类
class HasPtr{ public: HasPtr(const string &s = string()):ps(new string(s)), i(0) { cout<<"HasPtr(const string &s = string())"<<endl; } HasPtr(const HasPtr &hp) :ps(new string(*hp.ps)), i(hp.i) { cout<<"HasPtr(const HasPtr &hp)"<<endl; } HasPtr &operator=(const HasPtr &hp) { auto tmp = new string(*hp.ps); // 右侧对象拷贝到临时变量,考虑自己拷贝自己的情况。
delete ps; // 析构左侧对象
ps = tmp; i = hp.i; cout<<"HasPtr &operator="<<endl; return *this; } HasPtr testCopy(HasPtr hp) { return hp; } HasPtr &testCopy1(HasPtr &hp) { return hp; } ~HasPtr() { cout<<"~HasPtr()"<<endl; } void print() { cout<<*ps<<endl; } void setPs(const string &str) { *ps = str; } private: string *ps; int i; };
调试代码
HasPtr hp("hello"); // 调用构造函数,输出HasPtr(const string &s = string()) HasPtr hp1 = hp; // 调用拷贝构造函数,输出HasPtr(const HasPtr &hp) HasPtr hp2; // 调用构造函数,输出HasPtr(const string &s = string()) hp2 = hp; // 调用拷贝运算符函数,输出HasPtr &operator= hp1.setPs("word"); // 构造函数、拷贝构造函数、拷贝赋值运算符函数都为ps分配了新空间 hp.print(); // 所以hp、hp1、hp2的ps指针指向的空间不一样,修改一个的值其他的不变。 hp1.print(); // 输出word,由setPs函数修改 hp2.print(); // 输出hello,值从hp拷贝过来,空间是新分配的。 // 实参初始化形参要调用一次拷贝构造函数,形参初始化返回的临时变量也要调用一次拷贝构造函数, hp.testCopy(hp1); // 形参和临时变量在函数结束时时要销毁,调用两次析构函数。 hp.testCopy1(hp1); // 都是引用初始化,不需要调用拷贝构造函数。 HasPtr *hp_ptr = new HasPtr; // 调用构造函数 vector<HasPtr> vec; // 调用vector的默认构造函数。 vec.push_back(hp); // 调用拷贝构造函数 // 接下来要销毁hp,hp1,hp2和vector中的HasPtr的元素,要调用4次析构函数,但是hp_ptr指向的空间不会被销毁。
定义行为像指针的类
class HasPtr{ public: // 构造函数,为指针指向分配空间(整个类也只在这个地方分配空间),use初始化为1 HasPtr(const string &s = string()):ps(new string(s)), i(0) ,use(new size_t(1)){ cout<<"HasPtr(const string &s = string())-->use"<<*use<<endl; } // 拷贝构造函数,只拷贝指针,use值递增 HasPtr(const HasPtr &hp) :ps(new string(*hp.ps)), i(hp.i) ,use(hp.use){ ++*use; cout<<"HasPtr(const HasPtr &hp)-->use"<<*use<<endl; } // 拷贝赋值运算符,左侧use递减,当use为0,释放左侧指针指向空间,拷贝右侧数据和指针到左侧对象,use递增。 HasPtr &operator=(const HasPtr &hp) { if(--*use == 0) { delete ps; delete use; } ++*hp.use; ps = hp.ps; use = hp.use; i = hp.i; cout<<"HasPtr &operator=-->use"<<*use<<endl; return *this; } HasPtr testCopy(HasPtr hp) { return hp; } HasPtr &testCopy1(HasPtr &hp) { return hp; } // 析构函数,递减use,当use为0,释放指针指向空间。 ~HasPtr() { if(--*use == 0){ delete ps; delete use; } cout<<"~HasPtr()-->use"<<*use<<endl; } void print() { cout<<*ps<<endl; } void setPs(const string &str) { *ps = str; } private: string *ps; // 共享,只有构造函数分配,最后一次析构函数销毁 int i; // 每个对象独享,每次构造函数、拷贝构造函数、拷贝运算符都分配,每次析构都销毁 size_t *use; // 共享,只有构造函数分配,最后一次析构函数销毁 };
测试程序
HasPtr hp("hello"); // 调用构造函数,输出HasPtr(const string &s = string()) HasPtr hp1 = hp; // 调用拷贝构造函数,输出HasPtr(const HasPtr &hp) HasPtr hp2; // 调用构造函数,输出HasPtr(const string &s = string()) hp2 = hp; // 调用拷贝运算符函数,输出HasPtr &operator= hp1.setPs("word"); // 拷贝构造函数、拷贝赋值运算符不为指针ps和use分配新空间 hp.print(); // 所以hp、hp1、hp2的ps指针指向相同的空间,修改一个的值其他的也变。 hp1.print(); // 输出word,由setPs函数修改 hp2.print(); // 输出word,由setPs函数修改 // 实参初始化形参要调用一次拷贝构造函数,形参初始化返回的临时变量也要调用一次拷贝构造函数, hp.testCopy(hp1); // 形参和临时变量在函数结束时时要销毁,调用两次析构函数。 hp.testCopy1(hp1); // 都是引用初始化,不需要调用拷贝构造函数。 HasPtr *hp_ptr = new HasPtr; // 调用构造函数 vector<HasPtr> vec; // 调用vector的默认构造函数。 vec.push_back(hp); // 调用拷贝构造函数 // 接下来要销毁hp,hp1,hp2和vector中的HasPtr的元素,要调用4次析构函数,只有最后一次调用析构函数才会释放ps和use指向的空间。 // 但是hp_ptr指向的空间不会被销毁。