2013-06-16 34 views
12

Pracuję nad sposobem, aby uniemożliwić użytkownikowi korzystanie z klasy bez inteligentnych wskaźników. W ten sposób zmuszając ich do tego, aby obiekt był przydzielany do sterty i zarządzany przez inteligentne wskaźniki. W celu uzyskania takiego rezultatu, próbowałem następujące: Jak wymusić tylko instancję inteligentnych wskaźników dla klasy?

#include <memory> 
class A 
{ 
private : 
    ~A {} 
    // To force use of A only with std::unique_ptr 
    friend std::default_delete<A>; 
}; 

Ta praca dość dobrze, jeśli tylko chcą użytkownicy klasy zdolny do manipulowania instancji klasy przez std::unique_ptr. Ale to nie działa dla std::shared_ptr. Chciałbym wiedzieć, czy miałeś jakieś pomysły na takie zachowanie. Jedynym rozwiązaniem, które znalazłem robi co następuje (używając friend std::allocator_traits<A>; był unsufficient):

#include <memory> 
class A 
{ 
private : 
    ~A {} 
    // For std::shared_ptr use with g++ 
    friend __gnu_cxx::new_allocator<A>; 
}; 

Ale to rozwiązanie nie jest przenośny. Może robię to w niewłaściwy sposób.

+0

Ładne rozwiązanie ... OK, to nie będzie działać dla STL, ale zawsze chciałem tego dla mojej własnej implementacji SharedPtr i to rozwiązuje mój problem! – mmmmmmmm

Odpowiedz

17

Utwórz funkcję fabularną przyjaciela, która zwraca wartość std::unique_ptr<A>, i niech twoja klasa nie ma dostępnych konstruktorów. Ale uczynić destruktora dostępne:

#include <memory> 

class A; 

template <class ...Args> 
std::unique_ptr<A> make_A(Args&& ...args); 

class A 
{ 
public: 
    ~A() = default; 
private : 
    A() = default; 
    A(const A&) = delete; 
    A& operator=(const A&) = delete; 

    template <class ...Args> 
    friend std::unique_ptr<A> make_A(Args&& ...args) 
    { 
     return std::unique_ptr<A>(new A(std::forward<Args>(args)...)); 
    } 
}; 

Teraz klienci mogą oczywiście dostać unique_ptr<A>:

std::unique_ptr<A> p1 = make_A(); 

ale klienci mogą tak łatwo dostać shared_ptr<A>:

std::shared_ptr<A> p2 = make_A(); 

Ponieważ std::shared_ptr puszka być zbudowane z std::unique_ptr. A jeśli masz napisane przez użytkownika inteligentne kursory, wszystko co musisz zrobić, aby być interoperacyjne z systemem jest utworzyć konstruktor, że trwa std::unique_ptr, podobnie jak std::shared_ptr ma, a to jest bardzo łatwe do zrobienia:

template <class T> 
class my_smart_ptr 
{ 
    T* ptr_; 
public: 
    my_smart_ptr(std::unique_ptr<T> p) 
     : ptr_(p.release()) 
    { 
    } 
    // ... 
}; 
+1

Dobra odpowiedź! Dzięki, to jest o wiele czystsze niż moje rozwiązanie. – Nemikolh

+4

Gdy istnieje więcej klas 'B',' C' itd. Ze wspólnym interfejsem, który potrzebuje takich funkcji fabrycznych 'make_B()', 'make_C()', preferuję statyczne szablony publicznych nazw 'T :: make() '. Ułatwia to ustawienie pełnej fabryki i klasy rejestru, gdzie konkretne typy 'make()' mogą być rejestrowane z listy typów podczas kompilacji. – TemplateRex

+0

@TemplateRex: Zgoda. To dobra konwencja nazewnictwa dla funkcji fabrycznych. –

0

Ponieważ nie ma ogólnego określenia "inteligentny wskaźnik", to, czego się chce, nie jest możliwe.

Co można zrobić, obsługuje niektóre znane zestawy inteligentnych wskaźników. Zwykłe rozwiązanie zaczyna się jako twoje, czyniąc ctor lub dtor prywatnym i dodaje funkcje fabryczne. To może zwrócić instancję wypełnioną pożądanymi inteligentnymi wskaźnikami. Jeśli chcesz tylko obsługiwać unique_ptr i shared_ptr, to znaczy że masz dwie funkcje fabryczne, niewiele więcej. (zwróć uwagę, że te wskaźniki pozwalają przemycić surowy wskaźnik za pomocą prostego interfejsu, więc formant nie jest pełny).