2010-04-07 11 views
7

Si consideri il seguente:Posso usare boost :: make_shared con un costruttore privato?

class DirectoryIterator; 

namespace detail { 
    class FileDataProxy; 

    class DirectoryIteratorImpl 
    { 
     friend class DirectoryIterator; 
     friend class FileDataProxy; 

     WIN32_FIND_DATAW currentData; 
     HANDLE hFind; 
     std::wstring root; 

     DirectoryIteratorImpl(); 
     explicit DirectoryIteratorImpl(const std::wstring& pathSpec); 
     void increment(); 
     bool equal(const DirectoryIteratorImpl& other) const; 
    public: 
     ~DirectoryIteratorImpl() {}; 
    }; 

    class FileDataProxy //Serves as a proxy to the WIN32_FIND_DATA struture inside the iterator. 
    { 
     friend class DirectoryIterator; 
     boost::shared_ptr<DirectoryIteratorImpl> iteratorSource; 
     FileDataProxy(boost::shared_ptr<DirectoryIteratorImpl> parent) : iteratorSource(parent) {}; 
    public: 
     std::wstring GetFolderPath() const { 
      return iteratorSource->root; 
     } 
    }; 
} 

class DirectoryIterator : public boost::iterator_facade<DirectoryIterator, detail::FileDataProxy, std::input_iterator_tag> 
{ 
    friend class boost::iterator_core_access; 
    boost::shared_ptr<detail::DirectoryIteratorImpl> impl; 
    void increment() { 
     impl->increment(); 
    }; 
    bool equal(const DirectoryIterator& other) const { 
     return impl->equal(*other.impl); 
    }; 
    detail::FileDataProxy dereference() const { 
     return detail::FileDataProxy(impl); 
    }; 
public: 
    DirectoryIterator() { 
     impl = boost::make_shared<detail::DirectoryIteratorImpl>(); 
    }; 
}; 

Sembra DirectoryIterator dovrebbe essere in grado di chiamare boost::make_shared<DirectoryIteratorImpl>, perché è un amico di DirectoryIteratorImpl. Tuttavia, questo codice non riesce a compilare perché il costruttore di DirectoryIteratorImpl è privato.

Poiché questa classe è un dettaglio di implementazione interna che i client di DirectoryIterator non devono mai toccare, sarebbe bello se potessi mantenere privato il costruttore.

È questo il mio malinteso fondamentale attorno allo make_shared o devo contrassegnare una sorta di elemento di potenziamento come friend in modo che la chiamata possa essere compilata?

+0

Sei sicuro di aver bisogno di shared_ptr per il tuo puntatore impl? boost :: scoped_ptr è normalmente più appropriato e rende le cose molto più semplici. Shared_ptr verrebbe normalmente utilizzato solo in questo caso se si desidera che DirectoryIterator sia copiabile e che le copie condividano una singola istanza impl. Nel codice che hai postato, sembra che le copie che condividono un impl sarebbero un errore. Shared_ptr è per quando più puntatori devono condividere la proprietà di un'istanza. – Alan

risposta

5

Avrete infatti bisogno di fare alcuni pezzi boost amico per questo. Fondamentalmente lo make_shared chiama il costruttore e il fatto che ciò avvenga all'interno di una funzione friend non ha importanza per il compilatore.

La buona notizia è che lo make_shared chiama il costruttore, non un altro pezzo. Quindi, solo facendo make_shared amico avrebbe funzionato ... Tuttavia ciò significa che chiunque potrebbe quindi creare un shared_ptr<DirectoryIteratorImpl> ...

+1

Hmm ... è fastidioso da morire :) Grazie! –

+0

Il problema di 'make_shared' è che assegna un blocco di memoria e quindi usa il posizionamento' new', che è il motivo per cui deve invocare il costruttore da solo. Sono d'accordo è fastidioso per quanto riguarda il tuo problema. –

+1

Il problema con questa operazione è se si passa a TR1 o C++ 0x, o anche se boost rilascia un aggiornamento, non si ha alcuna garanzia che funzionerà ancora. – dvide

4

C'è un buon motivo per non utilizzare il buon vecchio shared_ptr costruttore? (Se c'è una, si potrebbe desiderare di dare un'occhiata al make_shared implementazione e farlo)

DirectoryIterator() 
    : impl(new detail::DirectoryIteratorImpl()) 
{} 

In questo modo la chiamata al costruttore è fatto dalla classe DirectoryIterator che è già amico di DirectoryIteratorImpl senza apertura la porta per tutti gli altri codici.

+0

No, non c'è niente di sbagliato in questo. Ma mi è stato detto di usare 'make_shared' su http://stackoverflow.com/questions/2569046/is-there-a-way-to-integrare- l'efficienza-di-shared-ptr-by-storing-the- riferimento/2569211 # 2569211. Quello che ho fatto per ora è semplicemente fatto esattamente come suggerisci tu. +1 –

+1

'make_shared' è più efficiente nelle sue allocazioni di memoria ... (meno frammentazione, maggiore velocità) –

+1

Conosco la frammentazione della memoria (dovresti davvero misurare), ma leggo qualche volta (in realtà qui in SO): * He chi sacrifica la correttezza per le prestazioni non merita nemmeno *, che è un bel motto. E nella domanda originale collegata, Billy ammette che non ha bisogno della performance. Se il costruttore è privato, 'make_shared' non dovrebbe essere un amico (che è molto vicino all'interrompere l'incapsulamento permettendo a chiunque di costruire l'oggetto tramite' make_shared') –

0

È possibile suddividere la classe in parte di interfaccia e parte di implementazione. La parte dell'interfaccia è resa pubblica e la parte di implementazione può avere costruttori pubblici. Tuttavia, ciò significa che è necessario utilizzare l'ereditarietà virtuale.

Problemi correlati