【问题标题】:C++ Assignment operator for class that contains a unique pointer member variable包含唯一指针成员变量的类的 C++ 赋值运算符
【发布时间】:2019-04-18 10:15:03
【问题描述】:

我了解,如果我有一个具有智能唯一指针的类,则无法将该类分配给另一个实例,因为无法复制唯一指针。我知道我可以使唯一指针成为共享指针,这将解决问题。但是,如果我不想共享指针的所有权怎么办?是否可以创建一个赋值运算符来移动唯一指针并复制其他变量?

我了解到您可以使用std::move 来传递所有权。

#include <iostream>
#include <memory> 

struct GraphStructure { };

class test {

        int a;
        std::vector<int> vector;
        std::unique_ptr<GraphStructure> Graph_; 
 };

 int main () {

     test t1;
     auto t2 = t1;
 }

【问题讨论】:

  • 不移动会使t1 没用吗?您想解决什么具体问题?
  • @πάνταῥεῖ 在给定的示例中移动没有意义,但与std::vector结合使用;我们需要搬家以便重新分配。
  • 您可以创建一个移动赋值运算符来移动所有内容(在这种情况下,使用test&amp; operator=(test&amp;&amp; other) = default;),但这也会移动向量而不是复制它。如果您手动实现它,您可以按照您的要求去做(您是否会因为尝试而陷入困境),但它不会像大多数使用您的课程的人所期望的那样,所以我强烈建议不要这样做。
  • 另外,在main 中,您从不使用赋值运算符。第二行调用复制构造函数(由于unique_ptr 而被隐式删除),而不是复制赋值运算符,即使它使用= 语法。
  • 您可能想复制另一个对象的Graph_ 拥有的GraphStructure,而不是窃取它。这是赋值的“正常”行为(和初始化,这就是你正在做的事情),如果你使用原始指针,你会做什么。

标签: c++ unique-ptr assignment-operator


【解决方案1】:

test 类的默认 copy 构造函数被删除,因为成员 (graph_) 不可复制(如果您仍然可以以任何有意义的方式复制,例如通过创建 deep 图成员的副本,您必须在自己的复制构造函数上实现)。相反,默认的 move 构造函数仍然存在(std::unique_ptr 是可移动的)。那么你可以做的事情如下:

test t1;
auto t2 = std::move(t1);

但请注意,t1 然后 不会 再持有任何对象(您 移动了该对象,因此您将其内容移动到另一个对象)并且之前由t2 持有的对象被销毁。如果这是一个有意义的状态由你决定......

旁注:我写的关于复制和移动构造函数的内容也适用于复制和移动赋值...

【讨论】:

    【解决方案2】:

    轻松解决此问题

    如果GraphStructure 是一个没有任何虚成员函数的类或结构,这很容易做到。我们可以编写一个函数来复制unique_ptr 中的数据以创建一个新的GraphStructure

    std::unique_ptr<GraphStructure> duplicate(std::unique_ptr<GraphStructure> const& ptr)
    {
        return std::make_unique<GraphStructure>(*ptr);
    }
    

    一旦我们有了duplicate,我们就可以使用这个类来编写一个拷贝构造函数进行测试:

    class test {
        std::unique_ptr<GraphStructure> ptr;
        std::vector<int> values;
       public:
        // this can be defaulted
        test() = default;
        // we use duplicate to create a copy constructor
        test(const test& source) 
          : ptr(duplicate(source.ptr)))
          , values(source.values)
        {}
        // we can use the default move constructor
        test(test&&) = default;
    
        test& operator=(test const& source) {
            ptr = duplicate(source.ptr);
            values = source.values; 
            return *this;
        }
        // we can use the default move assignment operator 
        test& operator=(test&&) = default;
    };
    

    如果 GraphStructure 有虚方法怎么办?

    在这种情况下,向 GraphStructure 添加一个虚拟的clone 方法,该方法返回一个新的std::unique_ptr&lt;GraphStructure&gt;

    class GraphStructure {
       public:
        // override this method in child classes
        virtual std::unique_ptr<GraphStructure> clone() {
            return std::make_unique<GraphStructure>(*this);
        }
        virtual ~GraphStructure() {}
    };
    

    然后,使用.clone() 代替duplicate

    【讨论】:

    • 您通常应该更喜欢make_unique 而不是unique_ptr&lt;T&gt;(new ...),因为如果表达式中出现问题,那么您的new 就没有对应的delete 并且您有内存泄漏。请参阅herbsutter.com/gotw/_102(除了make_unique 现在是标准函数,而不是您需要编写的东西)。
    • 如果new 成功完成,那么函数中就不会出现问题,因为标准保证 std::unique_ptr 的构造函数为 noexcept
    • @J.AntonioPerez 在这种情况下确实如此,因为表达式很简单。但是,如果您在具有其他未排序操作的表达式中使用原始指针构造函数,则可能会在 newunique_ptr 构造函数之间抛出一些东西并泄漏内存。您的代码中的这行代码没有错,只是没有加强良好的习惯,在向学习智能指针的人提供帮助时,这是一个问题。
    • “如果 GraphStructure 是一个没有任何虚成员函数的类或结构”。否则它应该提供一些Clone 方法。
    • 谢谢 - 我更新了它以显示一个 clone() 方法并使用 make_unique
    猜你喜欢
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 2021-06-28
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多