2013-06-16 16 views
12

Ho lavorato su un modo per impedire all'utente di utilizzare una classe senza puntatori intelligenti. Quindi, costringendoli a fare in modo che l'oggetto venga ammassato e gestito da puntatori intelligenti. Al fine di ottenere un risultato del genere, ho provato la seguente: Come forzare solo l'istanza di puntatori intelligenti per una classe?

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

Questo lavoro abbastanza bene se desideri solo gli utenti di classe di essere in grado di manipolare un'istanza della classe attraverso std::unique_ptr. Ma non funziona per std::shared_ptr. Quindi mi piacerebbe sapere se hai qualche idea per ottenere un simile comportamento. L'unica soluzione che ho trovato sta facendo la seguente (usando friend std::allocator_traits<A>; era unsufficient):

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

Ma questa soluzione non è portatile. Forse lo sto facendo nel modo sbagliato.

+0

Bella soluzione ... okay, questo non funzionerà per STL ma ho sempre voluto questo per la mia implementazione SharedPtr e questo risolve il mio problema! – mmmmmmmm

risposta

17

Creare una funzione di fabbrica amico che restituisce un std::unique_ptr<A> e rendere la classe non ha costruttori accessibili. Ma rendere il distruttore a disposizione:

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

Ora i vostri clienti possono ovviamente ottenere un unique_ptr<A>:

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

Ma i clienti possono altrettanto facilmente ottenere un shared_ptr<A>:

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

Perché std::shared_ptr può essere costruito da un std::unique_ptr. E se avete dei puntatori intelligenti scritti dall'utente, tutto quello che devono fare per essere interoperabile con il sistema è creare un costruttore che prende un std::unique_ptr, proprio come std::shared_ptr ha, e questo è molto facile da fare:

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

Bella risposta! Grazie, è molto più pulito della mia soluzione. – Nemikolh

+4

Quando ci sono più classi 'B',' C' ecc. Con un'interfaccia comune che richiede tali funzioni di fabbrica 'make_B()', 'make_C()', tendo a preferire i modelli statici dei membri pubblici 'T :: make() '. Ciò rende un po 'più facile impostare una classe factory + di registro completa, in cui le funzioni concrete 'make()' possono essere registrate da una lista di tipi in fase di compilazione. – TemplateRex

+0

@TemplateRex: concordato. Questa è una bella convenzione di denominazione per le funzioni di fabbrica. –

0

Poiché non esiste un termine generico "puntatore intelligente", ciò che si desidera non è possibile.

Quello che puoi fare, è supportare alcuni noti set di puntatori intelligenti. La solita soluzione inizia come la tua, rendendo privato il ctor o il dtor e aggiunge le funzioni di fabbrica. Ciò può restituire l'istanza piena di puntatori intelligenti desiderati. Se si desidera supportare unique_ptr e shared_ptr, ciò significa che due funzioni di fabbrica non sono sufficienti. (si noti che questi puntatori consentono il contrabbando del puntatore raw tramite un'interfaccia semplice in modo che il controllo non sia completo.)