来自《剑指offer》
如下为类型CMyString 的声明,请为该类添加赋值运算符函数。
class CMyString{
public:
cMyString(char* pData = NULL);
cMyString(const CMyString str);
~CMyString(void);
private:
char *m_pData;
}
在写赋值函数时,需要注意以下四点:
- 是否把返回值的类型声明为该类型的引用,并在函数结束前返回自身的引用(*this).只有返回是引用,才可以允许进行连续赋值,否则
str1 = str2 = str3
将会不能通过编译。 - 是否把传入的参数类型声明为常量引用。如果传入的参数不是引用而是实例,那么从形参到实参会调用一次复制构造函数。把参数定义为这样的引用可以避免这样的消耗,同时我们在赋值运算符函数内不会改变传入实例的状态,因此需要在传入的引用上面加上const关键字,我们需要养成这样的习惯。
- 是否释放实例自己的内层。如果我们忘记了在分配新内存前释放掉自己已有的空间,程序将出现内存泄漏
- 是否判断传入的参数和当前的实例(*this) 是不是同一个实例。如果是同一个,则不进行赋值操作,直接返回。如果实现不判断是否是同一个实例,那么在释放自己内存时会出现严重的问题,这会导致再也找不到要赋值的内容了。
CMyString& CMyString::operator = (const CMyString &str){
if(this == &str){
return *this;
}
delete []m_pData;
m_pData = NULL;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}
实现String类
接下来,我们来实现String类的普通构造函数,复制构造函数,赋值运算符、析构函数
#include<bits/stdc++.h>
using namespace std;
#define DEBUG
class String{
public:
String(const char *str = nullptr); // 通用构造函数
String(const String &rhs); // 赋值构造函数
~String(); // 析构函数
String& operator =(const String &rhs); // 赋值运算符
private:
char *m_data;
};
// 注意以下几点
// 1. 判断str是否为nullptr
// 2. 赋值前要对m_data分配内存
String::String(const char*str){
if(str == nullptr){
m_data = new char[1];
m_data[0] = ' ';
}
else{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
}
}
// 注意
// 1. 在析构函数中需要释放掉空间,防止内存泄漏
String::~String(){
delete []m_data;
m_data = nullptr;
}
// 注意以下几点
// 1. 还是得注意先分配内层
String::String(const String &rhs){
m_data = new char[strlen(rhs.m_data) + 1];
strcpy(m_data, rhs.m_data);
}
// 注意:
// 1. 返回值为引用
// 2. 参数为常引用
// 3. 赋值前要释放内层并重新申请内存
String& String::operator = (const String &str){
if(this == &str) return *this;
delete []m_data;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}
int main(){
#ifdef DEBUG
String str("hello wold!"); // 调用同通用构造函数
String str1 = str; // 调用赋值构造函数
String str2;
str2 = str1; // 赋值构造函数
#endif
return 0;
}