要点:
- 返回自身的引用(*this), 如果返回void则不能连续赋值,如
str1=str2=str3
不能通过编译。 - 传入参数为声明为常引用类型(const);
- 在分配内存前检查先释放自身已有内存,否则会出现严重的内存泄露问题;
- 判断传入的参数和当前实例(*this)是否为同一实例,如果是同一实力直接返回,不进行赋值操作。如果不判断,则会释放自身内存时会存在严重问题;
- 异常安全性的问题;
基本写法
MyString &operator=(const MyString &str)
{
if (m_pData == str.m_pData) {
return *this;
}
// 先delete是为了防止原来就有值,先把原来的内存释放掉,否则容易出现内存问题
delete[] m_pData;
m_pData = nullptr;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}
以上写法可以满足拷贝构造的基本需求,但存在一个问题,当delete自身内存后,在分配一个新的内存时,此时如果内存不足将会抛出一个异常终止此次内存分配。同时自身内存也没有了,为下面异常处理提供难度和不便
高级写法
MyString &operator=(const MyString &str)
{
if (this != &str) {
// 在没有析构自己前先分配内存,防止内存安全异常
MyString tmp(str);
char *pTmp = m_pData;
m_pData = tmp.m_pData;
tmp.m_pData = pTmp;
}
return *this;
}
在没有析构自己前先分配内存,如果此时分配内存失败(内存异常),那么自己的数据仍然没有丢失,后面便于异常处理。
与tmp交换指针后,tmp的内存为原来的内存,离开作用域后将被析构,而本身的m_pData所指向的新内存为由MyString tmp(str)构造出来的内存,如此完成了拷贝构造。
完整代码
#include <iostream>
#include <ostream>
#include "cstring"
class MyString
{
public:
MyString(char *pData = nullptr)
{
if (pData == nullptr) {
// 只声明,不赋值
m_pData = new char[1];
m_pData[0] = '\0';
} else {
// +1 是因为字符串最后一位时 '\0'
m_pData = new char[strlen(pData) + 1];
strcpy(m_pData, pData);
}
}
MyString(const MyString &str)
{
auto len = strlen(str.m_pData);
m_pData = new char[len + 1];
strcpy(m_pData, str.m_pData);
};
~MyString(void) { delete[] m_pData; }
// 初级写法
// MyString &operator=(const MyString &str)
// {
// if (m_pData == str.m_pData) {
// return *this;
// }
// // 先delete是为了防止原来就有值,先把原来的内存释放掉
// delete[] m_pData;
// m_pData = nullptr;
// m_pData = new char[strlen(str.m_pData) + 1];
// strcpy(m_pData, str.m_pData);
// return *this;
// }
MyString &operator=(const MyString &str)
{
if (this != &str) {
// 在没有析构自己前先分配内存,防止内存安全异常
MyString tmp(str);
char *pTmp = m_pData;
m_pData = tmp.m_pData;
tmp.m_pData = pTmp;
}
return *this;
}
friend std::ostream &operator<<(std::ostream &os, const MyString &str);
private:
char *m_pData;
};
// operator重载必须是全局函数,如果是类的成员函数,就变成data<<cout,与使用习惯相反
std::ostream &operator<<(std::ostream &os, const MyString &str)
{
os << str.m_pData;
return os;
}
int main(int argc, char *argv[])
{
char *c = "string";
MyString str(c);
MyString str2 = str;
std::cout << str << " " << str2 << std::endl;
return 0;
}