我有一个有自己的资源管理的课程:
class Lol
{
private:
// This is data which this class allocates
char *mName = nullptr;
public:
Lol(std::string str) // In constructor just copy data from string
{
auto cstr = str.c_str();
auto len = strlen(cstr);
mName = new char[len + 1];
strcpy(mName, cstr);
};
~Lol() // And of course clean up
{
delete[] mName;
}
}
我实现了复制构造函数,它只复制托管数据:
Lol(const Lol &other)
{
auto cstr = other.mName;
auto len = strlen(cstr);
mName = new char[len + 1];
strcpy(mName, cstr);
};
我还需要实现复制赋值运算符。我刚刚这样做了:
Lol &operator=(const Lol &other)
{
if (this == &other)
{
return *this;
}
// Clean up my resources
this->~Lol();
// And copy resources from "other" using already implemented copy constructor
new (this) Lol(other);
}
看起来这个复制赋值运算符适用于所有类。为什么复制赋值运算符中需要有另一个代码?它的用例是什么?
如果构造函数抛出异常,则必须捕获异常并以某种方式恢复(通过调用某个构造函数,可能是默认构造函数),这使得这变得不太优雅。未能调用构造函数将导致双重破坏和UB。
如果您从此类继承,或者将其用作[[no_unique_address]]
成员变量:
[basic.life]/8.4
对象 o1 可以透明地被对象 o2 替换,如果: ...
— o1 和 o2 都不是潜在重叠的子对象...
and [intro.object]/7
潜在重叠的子对象是:
— 基类子对象,或者
— 使用 no_unique_address 属性声明的非静态数据成员。
这本身不是 UB,但如果你的对象不是透明可更换,重建的对象必须是std::launder
在使用之前进行ed,这是不切实际的(例如,如果它是一个自动变量,那么自动销毁将会发生,而无需std::launder
→ UB).
C++17 还有更多限制。你还需要std::launder
如果您的类包含常量或引用成员(C++17 [basic.life]/8.3).
如果您正在寻找通用赋值运算符,那么这里就有一个。它被称为复制和交换习语。看哪:
MyClass &operator=(MyClass other) noexcept
{
std::swap(x, other.x); // Swap every member here.
return *this;
}
这充当复制和移动赋值(如果您有各自的构造函数),并提供强大的异常保证(如果复制抛出,目标对象不变)。
(据我所知)唯一不能开箱即用的情况是类在某处(可能在其内部)维护一个指向自身的指针。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)