【问题标题】:C++ copy constructor called at return在返回时调用 C++ 复制构造函数
【发布时间】:2019-08-12 19:06:59
【问题描述】:
error: use of deleted function 'A::A(const A&)'
 return tmp;
        ^~~

为什么只有A中有虚析构函数时才调用复制构造函数?如何避免这种情况?

struct B {};

struct A{
    std::unique_ptr<B> x;
    virtual ~A() = default;
};

A f() {
    A tmp;
    return tmp;
}

【问题讨论】:

  • C++ 处理与 C#/Java 不同的对象。当实例超出范围时(此处为tmp),必须调用其析构函数。因此,当您 return tmp 时,您要求它制作 tmp 的副本以返回给调用该函数的任何人。复制后,tmp 将被销毁,其副本将可供使用。
  • @Everyone 除了它通常是移动而不是复制,这就是问题所在。
  • 有点意外,因为我原以为会调用 RVO,导致没有移动或复制。

标签: c++


【解决方案1】:

virtual ~A() = default; 是用户声明的析构函数。因此,A 不再具有移动构造函数。这意味着 return tmp; 不能移动 tmp 并且由于 tmp 不可复制,您会收到编译器错误。

有两种方法可以解决此问题。您可以添加一个移动构造函数,如

struct A{
    std::unique_ptr<B> x;

    A() = default; // you have to add this since the move constructor was added
    A(A&&) = default; // defaulted move
    virtual ~A() = default;
};

或者你可以创建一个具有虚拟析构函数的基类并继承它

struct C {
    virtual ~C() = default;
};

struct A : C {
    std::unique_ptr<B> x;
};

这是因为A 不再有用户声明的析构函数(是的,C 有,但我们只关心A)所以它仍然会在A 中生成一个移动构造函数。重要的部分是C 没有删除的移动构造函数,它只是没有一个句点,所以试图移动它会导致复制。这意味着 C 的复制构造函数在 A 的隐式生成的移动构造函数中被调用,因为只要 C(std::move(A_obj_to_move_from)) 没有删除的移动构造函数,它就会复制。

【讨论】:

  • 最好遵循零/五规则。要么添加所有(复制 ctor、移动 ctor、复制赋值、移动赋值、析构函数),要么不添加任何一个。在此示例中,它们都不是必需的。
  • @0x5453 除非这是父类并且 OP 希望派生类被正确销毁。如果你有多态性,你需要一个虚拟析构函数。
  • @Tzalumen 不需要删除(因为这是唯一指针为您所做的),但需要一个虚拟析构函数,以便唯一指针不会有 UB。
  • @Tzalumen 如果您通过指向X 基类的指针deleteX 的对象并且该基类没有虚拟dtor,则它是未定义的行为。不管析构函数做什么。
  • @Tzalumen 如果你有多态性,你必须有一个虚拟析构函数。如果你不这样做,派生类的析构函数将不会被调用并且你有 UB。
猜你喜欢
  • 1970-01-01
  • 2021-03-23
  • 2010-10-14
  • 1970-01-01
  • 2016-08-12
  • 2013-12-08
  • 2013-10-03
  • 2021-01-04
  • 2011-07-17
相关资源
最近更新 更多