2010-04-07 23 views
7

considerar lo siguiente:¿Puedo usar boost :: make_shared con un constructor privado?

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

Parece que DirectoryIterator debe ser capaz de llamar a boost::make_shared<DirectoryIteratorImpl>, porque es un amigo de DirectoryIteratorImpl. Sin embargo, este código no se puede compilar porque el constructor de DirectoryIteratorImpl es privado.

Dado que esta clase es un detalle de implementación interna que los clientes de DirectoryIterator nunca deberían tocar, sería bueno si pudiera mantener el constructor privado.

¿Este es mi malentendido fundamental alrededor de make_shared o necesito marcar algún tipo de pieza de refuerzo como friend para que la llamada compile?

+0

¿Estás seguro de que necesitas shared_ptr para tu puntero impl? boost :: scoped_ptr es normalmente más apropiado y hace las cosas mucho más simples. Shared_ptr normalmente solo se usaría en este caso si quisiera que DirectoryIterator se pueda copiar y que las copias compartan una única instancia de impl. En el código que publicaste, parece que las copias que comparten un impl serían un error. Shared_ptr es para cuando varios punteros deben compartir la propiedad de una instancia. – Alan

Respuesta

5

Necesitarás hacer algunas piezas de impulso amigo para esto. Básicamente, make_shared está llamando al constructor y el hecho de que esto se hace desde una función de amigo no tiene importancia para el compilador.

La buena noticia es que make_shared llama al constructor, no a ninguna otra pieza. Por lo que sólo haciendo make_shared amigo funcionaría ... Sin embargo esto significa que cualquier persona podría entonces crear un shared_ptr<DirectoryIteratorImpl> ...

+1

Hmm ... eso es molesto como el infierno :) ¡Gracias! –

+0

El problema de 'make_shared' es que asigna un bloque de memoria y luego utiliza la ubicación' new', por lo que debe invocar el constructor por sí mismo. Estoy de acuerdo que es molesto con respecto a su problema. –

+1

El problema al hacer esto es si migra a TR1 o C++ 0x, o incluso si boost libera una actualización, no tiene garantía de que funcione. – dvide

4

¿Hay una buena razón para no usar el viejo shared_ptr constructor? (Si hay una, es posible que desee echar un vistazo a la implementación make_shared y lo hace)

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

De esta manera la llamada al constructor se hace de la clase DirectoryIterator que ya es un amigo de DirectoryIteratorImpl sin abrir la puerta para el resto del código.

+0

No, no tiene nada de malo. Pero me dijeron que usara 'make_shared' en http://stackoverflow.com/questions/2569046/is-herehere-a-way-to-increase-the-efficiency-of-shared-ptr-by-storing-the- referencia/2569211 # 2569211. Lo que hice por ahora simplemente se hace exactamente como sugieres. +1 –

+1

'make_shared' es más eficiente en sus asignaciones de memoria ... (menos fragmentación, mayor velocidad) –

+1

Sé acerca de la fragmentación de la memoria (realmente debe medir), pero leo en algún momento (en realidad aquí en SO): * He quien sacrifica la corrección por el desempeño no merece ninguno *, que es un buen lema. Y en la pregunta original vinculada, Billy admite que no necesita el rendimiento. Si el constructor es privado, 'make_shared' no debería ser un amigo (eso está muy cerca de romper la encapsulación al permitir que alguien construya el objeto a través de' make_shared') –

0

Puede dividir su clase en parte de la interfaz y parte de implementación. La parte de interfaz se hace pública, y la parte de implementación puede tener constructores públicos. Sin embargo, eso significa que debe usar herencia virtual.

Cuestiones relacionadas