二、类的构造与析构准则——三五法则
2024/8/14大约 2 分钟
类的构造与析构准则——三五法则

CppUpgrade
仓库地址
1.三法则
C++三法则:如果需要析构函数,则一定需要拷贝构造函数和拷贝赋值函数。
通常,若一个类需要析构函数,则代表其合成的析构函数不足以释放类所拥有的资源,其中最典型的就是指针成员。
一旦类中出现了指针类型的成员,我们必须防止浅拷贝问题。所以,一定需要拷贝构造函数和赋值操作符,这两个函数是防止浅拷贝问题所必须的。
以下面这个字符串为例,定义了基础的扩容和深拷贝函数:
class String
{
public:
//只扩容,不减容
void Reserve(std::size_t capacity)
{
if (this->capacity < capacity)
{
char* old_str = str;
str = new char[capacity];
this->capacity = capacity;
if (old_str)
{
memcpy(str, old_str, strlen(old_str) + 1);
delete[] old_str;
}
}
}
void DeepCopy(const String& other)
{
if (other.str)
{
std::size_t n = strlen(other.str) + 1;
Reserve(n);
memcpy(str, other.str, n);
}
else
{
if (str)
delete[] str;
str = nullptr;
capacity = 0;
}
}
public:
char* str = nullptr;//存放字符串的数据
std::size_t capacity = 0;//str数组的真实长度,计入'\0'
};遵循三法则,分别定义拷贝构造函数、拷贝赋值函数和析构函数
String(const String& other)
{
DeepCopy(other);
}
String& operator=(const String& other)
{
if (this != &other)
DeepCopy(other);
return *this;
}
~String()
{
if (str)
delete[] str;
}2.五法则
在较新的 C++11 标准中,为了支持移动语义(右值的引入),又增加了移动构造函数和移动赋值运算符,这样共有五个特殊的成员函数,所以又称为“C++五法则”;
与三法则不同的是,不提供移动构造函数和移动赋值运算符通常不是错误,但会导致失去优化机会。
移动构造、移动赋值要加上noexcept,用于通知标准库不抛出异常。
下面是移动构造、移动赋值函数的定义:
String(String&& other) noexcept :str(std::exchange(other.str,nullptr))
{
capacity = strlen(str) + 1;
}
String& operator=(String&& other) noexcept
{
std::swap(other.str, str);
capacity = strlen(str) + 1;
return *this;
}3.调用时机
| 函数 | 调用时机 |
|---|---|
| 拷贝构造函数 | 从已存在的左值对象直接构造不存在的对象 |
| 拷贝赋值构造函数 | 从已存在的左值对象赋值给不存在的对象 |
| 移动构造函数 | 从临时存在的右值对象构造不存在的对象 |
| 移动赋值函数 | 从临时存在的右值对象赋值给不存在的对象 |
