安全到标准:移动成员?

时间:2020-02-16 22:57:18

标签: c++

已经找到了可比的问题,但在这种情况下并不完全相同。 以下面的代码为例:

#include <iostream>
#include <string>
#include <vector>

struct Inner
{
    int a, b;
};

struct Outer
{
    Inner inner;    
};


std::vector<Inner> vec;

int main()
{
    Outer * an_outer = new Outer;

    vec.push_back(std::move(an_outer->inner));

    delete an_outer;
}

这样安全吗?即使这些是多态类还是带有自定义析构函数的类?

我担心的是“ Outer”实例的成员变量“ inner”已移开的情况。据我了解,搬动的东西不应该再被触碰了。但是,这是否包括应用于outer的delete调用,并且在技术上也将在inner上调用delete(因此“触摸”它)?

2 个答案:

答案 0 :(得分:2)

std::move和更一般的移动语义都不会对对象模型产生任何影响。它们不会停止存在的对象,也不会阻止您将来使用这些对象。

他们要做的是要求从您“搬家”的东西中借用封装的资源。例如,vector仅直接将一些动态分配的数据存储在指针中:通过简单地复制该指针然后告诉vector使该指针为空,可以“窃取”该数据的所有权概念。指针,再也不会与该数据有任何关系。屈服了。数据现在属于您。您拥有宇宙中存在的最后一个指针。

所有这些都是通过一堆黑客来实现的。第一个是std::move,它仅将您的vector表达式转换为vector&&,因此,当将其结果传递给构造或赋值操作时,该版本将使用vector&& (移动构造函数或移动分配运算符)被触发,而不是采用const vector&的那个,并且该版本执行执行我在上一段中所述的必要步骤。

(对于我们制作的其他类型,我们通常会遵循该模式,因为这样我们才能拥有美好的事物并说服人们使用我们的库。)

但是您仍然可以使用向量!您可以“触摸”它。在vector的文档中可以找到您可以完全使用的方法,并且该方法可以扩展到任何其他可移动类型:对从源对象的使用所施加的约束完全取决于其类型和决策由设计该类型的人制作。

这些都不会影响vector的生存期。它仍然存在,它仍然需要内存,并且当时间到来时它仍然会被破坏。 (在这个特定示例中,您实际上可以.clear()进行操作,然后再次开始将数据添加到新缓冲区中。)

因此,即使int对此有某种概念(它们没有;它们没有封装任何间接存储的数据,也没有资源;它们没有构造函数,所以它们也没有构造函数用int&&),delete“触摸”它们将是完全安全的。而且,更笼统地说,这都不取决于您搬出的东西是否是会员。

更一般地说,如果您有一个类型T和一个该类型的对象,然后又将其移开,那么T的约束之一就是您不能{{1 }}移走后,可能是delete 中的错误。 T的作者那将是一个严重的错误。您的对象都必须是可破坏的。该错误可能表现为编译失败,或者更可能是不确定的行为,具体取决于错误的确切含义。

tl; dr:是的,出于多种原因,这是安全的。

答案 1 :(得分:0)

std::move是对右值引用的强制转换,它主要更改选择哪个构造函数/赋值运算符重载。在您的示例中,移动构造函数是默认生成的移动构造函数,它仅复制ints,因此没有任何反应。

这通常是否安全取决于您的班级实现移动构造/分配的方式。例如,假设您的班级持有一个指针。您必须在move-from类中将其设置为nullptr,以免破坏被指向的数据(如果move-from类被破坏了)。

由于 just 定义移动语义是一种自定义方式,几乎总是会导致问题,因此 5条规则表示,如果您自定义以下任何一项:

  • 复制构造函数
  • 副本分配运算符
  • 移动构造函数
  • 移动分配运算符
  • 破坏者

您通常应该自定义所有内容,以确保它们的行为与呼叫者通常对您的班级的期望保持一致。

相关问题