【问题标题】:Copy constructor with smart pointer使用智能指针复制构造函数
【发布时间】:2011-11-29 18:48:32
【问题描述】:

我的班级有一个std::unique_ptr 作为班级成员。我想知道如何正确定义复制构造函数,因为我收到以下编译器错误消息:error C2248: std::unique_ptr<_Ty>::unique_ptr : cannot access private member declared in class 'std::unique_ptr<_Ty>。我的班级设计看起来像:

template <typename T>
class Foo{
    public:
        Foo(){};
        Foo( Bar<T> *, int );
        Foo( const Foo<T> & );
        ~Foo(){};

        void swap( Foo<T> & );
        Foo<T> operator = ( Foo<T> );

    private:
        std::unique_ptr<Bar> m_ptrBar;
        int m_Param1;

};

template < typename T >
Foo<T>::Foo( const Foo<T> & refFoo )
:m_ptrBar(refFoo.m_ptrBar), 
m_Param1(refFoo.m_Param1)
{
    // error here!
}

template < typename T >
void Foo<T>::swap( Foo<T> & refFoo ){
    using std::swap;
    swap(m_ptrBar, refFoo.m_ptrBar);
    swap(m_Param1, refFoo.m_Param1);
 }

 template < typename T >
 Foo<T> Foo<T>::operator = ( Foo<T> Elem ){
    Elem.swap(*this);
    return (*this);
 }

【问题讨论】:

    标签: c++ templates smart-pointers copy-constructor


    【解决方案1】:

    假设目标是复制构建唯一拥有的 Bar,

    template < typename T >
    Foo<T>::Foo( const Foo<T> & refFoo )
    : m_ptrBar(refFoo.m_ptrBar ? new Bar(*refFoo.m_ptrBar) : nullptr),
      m_Param1(refFoo.m_Param1)
    {
    }
    

    【讨论】:

    • @Cubbi,谢谢。我现在有另一个问题。 Bar 类实际上是一个抽象基类,因此我收到一条新的错误消息:error C2259: 'Bar' : cannot instantiate abstract class,除了将抽象基类转换为简单基类之外,还有什么解决方案吗?
    • @Tin:在这种情况下,您需要将纯虚拟 clone() 函数添加到基类中,并在每个派生类中重写以使用 new 创建副本。然后初始化器变成bar(foo.bar ? foo.bar-&gt;clone() : nullptr)
    • @Tin C++FAQ 调用 "virtual constructor"
    • @MikeSeymour @Cubbi 如果我现在有一个带有私有成员的类:std::vector&lt;std::unique_ptr&lt;Base&gt; &gt; mvector;,并且想定义复制构造函数和赋值运算符,那么我需要逐个元素复制调用'clone()' virtual function,对吧?因为如果我遵循参数化列表中复制构造函数的传统实现,即MyCollection&lt;T&gt;::MyCollecyion(const refCol):myvector(refCol.myvector){},我会收到编译器错误。
    • @Cubbi,如果我现在只想std::move 指针呢?我试过了。喜欢m_ptrBar(std::move(refFoo.m_ptrBar)),但没有成功。有什么建议吗?
    【解决方案2】:

    Unique_ptr 文档:

    Stores a pointer to an owned object. The object is owned by no other unique_ptr. 
    The object is destroyed when the unique_ptr is destroyed.
    

    你不能复制它,因为两个对象不能拥有它。

    尝试切换到 std::shared_ptr。

    EDIT 我应该指出,这会使两个对象都有一个指向同一个对象的指针。如果您想复制唯一拥有的对象,Cubbi 的解决方案是正确的。

    【讨论】:

    • @w00te,谢谢。还有一个问题。如果 Bar 类实际上是一个抽象基类怎么办?我收到一条新的错误消息:error C2259: 'Bar' : cannot instantiate abstract class。除了将抽象基类转为简单基类之外,有什么解决办法吗?
    • Cubbi 的解决方案创建了一个新的 Bar 对象以包含在新类的 unique_ptr 中。如果 bar 是抽象的,那么那将无法工作 - 它必须创建一个适用的派生类的新对象。你必须添加逻辑来实现这一点。
    【解决方案3】:

    一种可能性是为此创建一个新的clone_ptr 类型。

    下面是clone_ptr 的基本示例,它调用派生对象的正确复制构造函数(和析构函数)。这是通过在创建 clone_ptr 时创建“类型擦除”帮助程序来完成的。

    其他实现可以在 Internet 上找到。

    #include <memory>
    
    namespace clone_ptr_detail
    {
    template <class T>
    class clone_ptr_helper_base
    {
    public:
        virtual ~clone_ptr_helper_base() {}
        virtual T* clone(const T* source) const = 0;
        virtual void destroy(const T* p) const = 0;
    };
    
    template <class T, class U>
    class clone_ptr_helper: public clone_ptr_helper_base<T>
    {
    public:
        virtual T* clone(const T* source) const
        {
            return new U(static_cast<const U&>(*source));
        }
        virtual void destroy(const T* p) const
        {
            delete static_cast<const U*>(p);
        }
    };
    }
    
    template <class T>
    class clone_ptr
    {
        T* ptr;
        std::shared_ptr<clone_ptr_detail::clone_ptr_helper_base<T>> ptr_helper;
    public:
        template <class U>
        explicit clone_ptr(U* p): ptr(p), ptr_helper(new clone_ptr_detail::clone_ptr_helper<T, U>()) {}
    
        clone_ptr(const clone_ptr& other): ptr(other.ptr_helper->clone(other.ptr)), ptr_helper(other.ptr_helper) {}
    
        clone_ptr& operator=(clone_ptr rhv)
        {
            swap(rhv);
            return *this;
        }
        ~clone_ptr()
        {
            ptr_helper->destroy(ptr);
        }
    
        T* get() const { /*error checking here*/ return ptr; }
        T& operator* () const { return *get(); }
        T* operator-> () const { return get(); }
    
        void swap(clone_ptr& other)
        {
            std::swap(ptr, other.ptr);
            ptr_helper.swap(other.ptr_helper);
        }
    };
    

    查看用法示例:http://ideone.com/LnWa3

    (但也许你真的不需要复制你的对象,而可能更愿意探索移动语义的可能性。例如,你可以有一个vector&lt;unique_ptr&lt;T&gt;&gt;,只要你不这样做'不要使用复制内容的函数。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-13
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多