2011-11-29 59 views
6

我有一個類,其中一個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); 
} 

回答

5

假設我們的目標是複製構造獨特,擁有酒吧,

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) 
{ 
} 
+0

@ Cubbi,謝謝。我現在有另一個問題。 Bar類實際上是一個抽象基類,因此我得到一個新的錯誤信息:'error C2259:'Bar':不能實例化抽象類',除了將抽象基類轉換爲一個簡單的基類? – Tin

+2

@Tin:在這種情況下,您需要爲基類添加一個純虛擬'clone()'函數,並在每個派生類中重寫以使用'new'創建一個副本。然後初始化者變成'bar(foo.bar?foo.bar-> clone():nullptr)'。 –

+0

@Tin C++ FAQ調用[「虛構構造函數」](http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8) – Cubbi

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。

編輯我應該指出,這將使兩個對象都有一個指向同一對象的指針。如果您想複製獨特擁有的對象Cubbi的解決方案是正確的。

+0

@ w00te,謝謝。還有一個問題。如果Bar類實際上是一個抽象基類呢?我收到一條新的錯誤消息:'錯誤C2259:'Bar':無法實例化抽象類'。除了將抽象基類轉換爲簡單的基類之外,是否還有解決方案? – Tin

+0

Cubbi的解決方案創建一個新的Bar對象,將其包含在新類的unique_ptr中。如果bar是抽象的,那麼它就無法工作 - 它必須創建一個適用派生類的新對象。你必須添加邏輯來實現這一點。 –

2

一種可能性是爲此創建一個新的clone_ptr類型。

下面是一個clone_ptr的基本示例,它調用派生對象的正確拷貝構造函數(和析構函數)。這是通過在創建clone_ptr時創建「類型擦除」助手來完成的。

其他實現可以在因特網上找到。

#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<unique_ptr<T>>。 ,只要你不使用複製內容的功能。)