gpt4 book ai didi

c++ - 这样写作业有什么问题?

转载 作者:行者123 更新时间:2023-11-30 02:48:50 25 4
gpt4 key购买 nike

前几天我和我的一个 friend 就对象分配和构造进行了一次谈话,他指出对象的分配 a = b (在语义上)等同于销毁 a 然后从 b 重新构建它(在同一个地方)。

当然,没有人(我认为)会这样写赋值运算符:

class A {
A& operator=(const A& rhs) {
this->~A();
this->A(rhs);
return *this;
}

A& operator=(A&& rhs) {
this->~A();
this->A(std::move(rhs));
return *this;
}

// etc.
};

[注意:我不知道如何在现有对象上手动调用构造函数/析构函数(我从来没有这样做过!),所以它们的调用可能没有正式意义,但我想你可以看出这个想法。]

这种方法有什么问题?我想必须有一个主要的表演终结者,但名单越大越好。

最佳答案

这里有一个误用的结构:

class A {
A& operator=(const A& rhs) {
if(&a==this) return *this;
this->~A();
new(this) A(rhs);
return *this;
}

A& operator=(A&& rhs) {
if(&a==this) return *this;
this->~A();
new(this) A(std::move(rhs));
return *this;
}

// etc.
};

这是对 inplace ctor/dtor 语义的正确尊重,这就是 std::allocator 用来销毁和构造缓冲区中的元素的操作,因此必须是正确的,对吧?

嗯……不恰当:这一切都是关于 A 实际上包含什么以及 A ctor 实际上做了什么。

如果 A 仅包含基本类型并且不拥有资源,那很好,它可以工作。这不是惯用的,而是正确的。

如果A包含一些其他资源,需要很好地获取、管理和释放……你可能会遇到麻烦。如果 A 是多态的(如果 ~A 是虚拟的,你会破坏整个对象,但随后你只重建 A 子对象),你也是。

问题是获取资源的构造函数可能会失败,构造失败并抛出的对象不能被销毁,因为它从未被“构造”过。

但是如果你是在“赋值”,你不是在“创建”,如果就地构造函数失败,你的对象将存在(因为它预先存在于它自己的范围内),但处于不能通过进一步的破坏来管理:认为

{
A a,b;
a = b;
}

处,b 和 a 将被销毁,但如果 A(const A&) 在 a=b 中失败,并且在 A 中产生了一个throw: :Aa不存在,但会在throw立即跳转到的处被不当销毁。

一个更惯用的方式是让

class A
{
void swap(A& s) noexcept
{ /* exchanging resources between existing objects should never fail: you just swap pointers */ }
public:
A() noexcept { /* creates an object in a "null" recognizable state */ }
A(const A& s) { /* creates a copy: may fail! */ }
A(A&& s) noexcept { /*make it as null and... */ swap(s); } // if `s` is temporary will caryy old resource deletionon, and we keep it's own resource going
A& operator=(A s) noexcept { swap(s); return *this; }
~A() { /* handle resource deletion, if any */ }
};

现在,

  a=b

将创建一个 b copy 作为 operator= 中的 s 参数(通过 A::A(const A&)).如果失败,s 将不存在并且 ab 仍然有效(具有它们自己的旧值),因此在范围内退出将是像往常一样销毁。如果复制成功,复制的资源和实际的a将交换,当s处死亡时,old-a资源将被释放。

通过交谈

a = std::move(b)

将使 b 成为临时的,s 参数通过 A(A&&) 构造,因此 b 将与 s 交换(并变为 null)而不是 s 将与A。最后,s 将销毁旧的 a 资源,a 将接收旧的 b,b 将是处于空状态(因此它可以在其作用域结束时平静地死去)

A()A(A&&) 都必须实现“使A 为null”的问题。这可能是通过帮助成员(init,就像 swap)或通过指定成员初始值设定项,或通过为成员定义默认初始化值(一劳永逸) )

关于c++ - 这样写作业有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21773566/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com