一、Big three(拷贝构造、拷贝赋值、析构函数)(video7)
Big three指三个特殊函数,分别是拷贝构造函数、拷贝赋值和析构函数。
什么时候需要拷贝构造、拷贝赋值、析构函数:
当类中的数据是指针时,例如string类中保存字符串使用char *,那么就不能直接使用编译器给的默认Big three。因为默认的函数是按字节拷贝的,这样拷贝后的对象中的指针指向的位置和被拷贝的对象一样,这样不是真正的拷贝。
class mystring { public: //普通构造函数 mystring(const char* chr = 0); //拷贝构造函数 mystring(const mystring& mstr); //析构函数 ~mystring(); //拷贝赋值 mystring& operator = (const mystring& mstr) { //检测是否为自我赋值 if (this == &mstr) { return *this; } //先将已有的空间释放,否则会内存泄漏 delete[] mychar; //创建新的空间,大小和mstr.mychar指向空间一样大 mychar = new char[strlen(mstr.mychar) + 1]; //赋值内容 strcpy(mychar, mstr.mychar); //返回等号左边的mystring对象,以防连续赋值 return *this; } private: //变量是指针,必须实现拷贝赋值和拷贝构造 char * mychar; }; inline mystring::mystring(const char* chr) { //判断传入的指针是否为空(或默认为空) if (chr) { //如果不为空,则按chr的大小分配空间,并让mychar指向该空间 mychar = new char[strlen(chr) + 1]; //将chr的数据复制到mychar指向的空间中 strcpy(mychar, chr); //如果指针为空 }else { //创建一个大小为1的空间 mychar = new char[1]; //只保存一个\0 *mychar = '\0'; } } inline mystring::mystring(const mystring& mstr) { //分配一个和mstr.mychar字符串一样大的空间,并让mychar指向该空间 mychar = new char[strlen(mstr.mychar) + 1]; //将内容复制到mychar指向的空间中 strcpy(mychar, mstr.mychar); } inline mystring::~mystring() { //当对象生命周期要结束的时候,必须清理内存(堆空间),否则会内存泄漏 delete[] mychar; }
在上述代码中,拷贝构造函数做的事情实际上是深拷贝,而默认的拷贝构造函数做的事情是浅拷贝(只复制mstr.mychar指针的4byte到mychar中)。如下图所示:
代码中,以下部分非常重要:
//检测是否为自我赋值 if (this == &mstr) { return *this; }
如果this和mstr是同一个对象,那么如果没有自我赋值的检测,可能会导致程序出错。
因为我们在拷贝数据之前,第一步就是先delete[] mychar,那么也就是删除了mstr.mychar指向的空间。
第二步我们要参照mstr.mychar指向空间的大小来开辟空间就会出问题,更别提复制其中的内容。
所以,自我赋值检测非常重要。
二、为mystring类添加<<重载函数
//定义成员方法,获取mychar指针,不修改数据,加上const inline char * mystring::get_c_str() const { return this->mychar; } //重载操作符<<,使可以直接cout<<mystring_obj; inline ostream& operator << (ostream& os, const mystring& mstr) { os << mstr.get_c_str(); return os; }
三、