C++ 如何将对象移动到 nullptr

C++ how to move object to a nullptr

我在想一个奇怪的用例,我想将一个对象移动到 nullptr。也许我应该给出一个代码片段:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&&b) : first(&a), second(&b) { }
   private:
       Obj* first;
       Obj* second;
};

问题是当a和b超出范围时,第一个和第二个指针将悬空。如果我可以将对象 a 移动到第一个指针上,那么应该没有双重释放和范围问题。如果成员 first 被声明为 Obj 而不是 Obj* 指针,那么直接 first(std::move(a)) 就可以完成这项工作。我一定是在这里做错了什么。我正在考虑移动而不是复制,因为我正在尝试将控制权从另一个对象转移到当前对象并提高性能。

使用指针版本是因为我正在考虑成员对象的多态行为。

您不能将对象移动到指针(nullptr 或其他)上。您也不能通过让指针指向对象来延长对象的生命周期 - C++ 根本无法那样工作。

由于您需要多态行为,您不能只是复制构造或分配一个 Obj 指针。正如这个问题中所讨论的,您可能需要 class Objclone() 方法之类的东西:

Copying derived entities using only base class pointers, (without exhaustive testing!) - C++

了解更多信息。不过请记住——您无法延长右值引用的生命周期,因此您必须以某种方式构造一个新对象。还要记住避免内存泄漏。所以如果你的 clone() 产生一个 Obj*,你需要写这样的东西:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&&b) : 
         first(a.clone()), 
         second(b.clone()) 
      { }
   private:
      std::unique_ptr<Obj> first;
      std::unique_ptr<Obj> second;
};

也许 std::shared_ptr's instead of the unique_ptr's (see this answer 关于在两者之间进行选择)。

如果您需要 "polymorphic" 行为,但您在编译时知道 确切 类型的 a 和 b,并且您不介意有不同的 ObjPair classes,你也可以尝试这样的事情:

template <typename T1, typename T2>
class Objpair {
   public:
      Objpair(T1&& a, T2&&b) : first(a), second(b) 
      { }
   private:
      T1 first;
      T2 second;
};

在这种情况下不涉及指针 (!)

在评论中,您说,

first and second can point to BaseClass, Derived1, Derived2,

在这种情况下,您最好的选择是在 Obj 中提供一个 virtual 成员函数来克隆一个对象。

class Obj
{
   public:
      virtual Obj* clone() const = 0;
   ...
}

并用它来初始化firstsecond

 class Objpair {
   public:
      Objpair(Obj&& a, Obj&&b) : first(a.clone()), second(b.clone()) { }
   private:
       Obj* first;
       Obj* second;
};

并确保在析构函数中处理 firstsecond 指向的内存的释放。有关详细信息,请参阅 The Rule of Three

您可以使用智能指针来消除 class 中用于管理对象生命周期的代码的需要。使用 std::unique_ptr 还是 std::shared_ptr 取决于您希望如何使用 Objpair.

您可以做的是从您的参数中移动构造 对象,如下所示:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&& b)
      : first(new Obj(std::move(a)))
      , second(new Obj(std::move(b)))
      {}

   private:
       Obj* first;
       Obj* second;
};

我推荐你使用std::unique_ptr:

class Objpair {
   public:
      Objpair(Obj&& a, Obj&& b)
      : first(std::make_unique<Obj>(std::move(a)))
      , second(std::make_unique<Obj>(std::move(b)))
      {}

   private:
       std::unique_ptr<Obj> first;
       std::unique_ptr<Obj> second;
};

@Galik 接受的答案很好。虽然它要求 Obj 至少是可移动的,这与 Clone() 解决方案的其他答案类似 - 他们都对 Obj 及其后代提出了一些要求。要删除此限制,我们可以更改 Objpair 的构造函数以接受 std::unique_ptr<Obj>:

#include <memory>

class Obj
{
public:
    Obj() {}

    // non-copyable/non-movable
    Obj(const Obj&) = delete;
    Obj& operator=(const Obj&) = delete;
};

class Objpair {
   public:
      Objpair(std::unique_ptr<Obj>&& a, std::unique_ptr<Obj>&& b)
      : first(std::move(a))
      , second(std::move(b))
      {}

   private:
       std::unique_ptr<Obj> first;
       std::unique_ptr<Obj> second;
};

int main()
{
    Objpair p{std::make_unique<Obj>(), std::make_unique<Obj>()};
    return 0;
}

可能的奖金是:

  • 用户处理内存分配,因此可以更好地控制它。
  • 用户可以在将 ab 参数传递给 Objpair 之前使用多态的实例。