2011-11-29 13 views
6

Mam klasy z jednym std::unique_ptr jako członka klasy. Zastanawiam się, jak poprawnie zdefiniować konstruktora kopiowania, ponieważ otrzymuję następujący komunikat o błędzie kompilatora: error C2248: std::unique_ptr<_Ty>::unique_ptr : cannot access private member declared in class 'std::unique_ptr<_Ty>. Moja klasa konstrukcja wygląda mniej więcej tak:Kopiuj konstruktora z inteligentnym wskaźnikiem

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); 
} 

Odpowiedz

5

Zakładając, że cel jest kopia skonstruować wyjątkowo wtórny 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) 
{ 
} 
+0

@ Cubbi, dzięki. Mam teraz inny problem. Klasa 'Bar' jest w rzeczywistości abstrakcyjną klasą bazową, dlatego otrzymuję nowy komunikat o błędzie:' error C2259: 'Bar': nie można utworzyć instancji klasy abstrakcyjnej', Czy istnieje rozwiązanie, oprócz zamiany abstrakcyjnej klasy bazowej, na prosta klasa podstawowa? – Tin

+2

@ Tin: w takim przypadku będziesz musiał dodać czystą wirtualną funkcję 'clone()' do klasy bazowej, nadpisaną w każdej klasie pochodnej, aby utworzyć kopię za pomocą 'new'. Następnie inicjatorem staje się 'bar (foo.bar? Foo.bar-> clone(): nullptr)'. –

+0

@Tin C++ FAQ wywołuje, że ["wirtualny konstruktor"] (http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8) – Cubbi

2

Unique_ptr dokumentacja:

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. 

cant skopiować go, ponieważ dwa obiekty nie mogą go posiadać.

Spróbuj przejść na std :: shared_ptr.

EDYCJA Należy zauważyć, że spowodowałoby to, że oba obiekty mają wskaźnik do tego samego obiektu. Jeśli chcesz skopiować unikatowy obiekt, rozwiązanie Cubbi jest poprawne.

+0

@ w00te, dzięki. Jeszcze jedno pytanie. Co się stanie, jeśli klasa Bar jest rzeczywiście abstrakcyjną klasą bazową? Otrzymuję nowy komunikat o błędzie: 'error C2259: 'Bar': nie można utworzyć instancji klasy abstrakcyjnej'.Czy istnieje jakieś rozwiązanie, oprócz zamiany abstrakcyjnej klasy bazowej na prostą klasę podstawową? – Tin

+0

Rozwiązanie Cubbi tworzy nowy obiekt Bar, który będzie zawarty w unique_ptr w nowej klasie. Jeśli pasek jest abstrakcyjny, to nie może on działać - musiałby zamiast tego utworzyć nowy obiekt odpowiedniej klasy pochodnej. Aby to osiągnąć, musisz dodać logikę. –

2

Istnieje możliwość utworzenia nowego typu: .

Poniżej znajduje się podstawowy przykład clone_ptr, który wywołuje prawidłowy konstruktor kopii (i destruktor) obiektu pochodnego. Odbywa się to tutaj, tworząc pomocnika "wymazanie typu", gdy tworzony jest clone_ptr.

Inne implementacje można znaleźć w Internecie.

#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); 
    } 
}; 

Zobacz przykład wykorzystania: http://ideone.com/LnWa3

(Ale może tak naprawdę nie potrzeba do kopia obiektami, a może raczej zbadać możliwości semantyki ruch dla przykładu, można mieć vector<unique_ptr<T>>. , o ile nie używasz funkcji, które kopiują zawartość.)