2010-08-22 17 views
5

Dada una interfaz abstracta y una implementación derivada de esa interfaz, donde los constructores están protegidos (la creación de estos objetos solo está disponible en una fábrica de clases para implementar un patrón DI), ¿cómo puede Hago uso de make_shared en la función de fábrica?Uso de make_shared con un constructor protegido + interfaz abstracta

Por ejemplo:

class IInterface 
{  
public:  
    virtual void Method() = 0; 
}; 

class InterfaceImpl : public IInterface 
{ 
public: 
    virtual void Method() {} 

protected:  
    InterfaceImpl() {}  
}; 

std::shared_ptr<IInterface> Create() 
{ 
    std::shared_ptr<IInterface> object = std:: make_shared<InterfaceImpl>();  
    return object; 
} 

make_shared obviamente no puede acceder al constructor protegida en InterfaceImpl, o incluso en IInterface, y me da el siguiente error


error C2248: 'InterfaceImpl::InterfaceImpl' : cannot access protected member declared in class 'InterfaceImpl' 

Así leer aquí (pregunta: How to make boost::make_shared a friend of my class), Intenté incluir lo siguiente en la clase de implementación:


friend std::shared_ptr<InterfaceImpl> std::make_shared<InterfaceImpl>(); 

Aún no compilaría. Así que también puse otro en la clase IInterface. Todavía no hay alegría. ¿Qué he hecho mal aquí?

EDIT: archivo de código fuente completo utilizado para compilar, con "amigo" ...

#include <memory> 

class IInterface 
{  
public:  
    friend std::shared_ptr&lt;IInterface> Create();  
    virtual void Method() = 0; 
}; 

class InterfaceImpl : public IInterface 
{  
public:  
    virtual void Method() {} 

protected:  
    friend std::shared_ptr&lt;IInterface> Create();  
    InterfaceImpl() {}  
}; 

std::shared_ptr<IInterface> Create() 
{ 
    std::shared_ptr<IInterface> object = std::make_shared<InterfaceImpl>();  
    return object; 
} 

void main() 
{ 
    std::shared_ptr<IInterface> i = Create(); 
} 
+0

Supongo que es VC10? GCC btw no tiene problemas siempre y cuando se haga amigo de 'make_shared()'. –

+0

Es VS2010, que en realidad da una advertencia (erróneamente, se detalla aquí: http://connect.microsoft.com/VisualStudio/feedback/details/321690/c-vc9-bogus-warning-c4396-for-valid-code). – Robinson

Respuesta

4

Con VC10 la solución se enlazó a no funciona - la construcción de la instancia de InterfaceImpl no sucede en make_shared, pero en un tipo interno en std::tr1::_Ref_count_obj<Ty>::_Ref_count_obj(void).

que acababa de hacer la función Create() un friend en su caso y no uso make_shared():

class InterfaceImpl : public IInterface { 
// ...  
protected: 
    friend std::shared_ptr<IInterface> Create(); 
    InterfaceImpl() {} 
}; 

std::shared_ptr<IInterface> Create() { 
    return std::shared_ptr<IInterface>(new InterfaceImpl()); 
} 

... o utilizar una aplicación personalizada make_shared() que realmente puede hacerse amigo sin depender de la aplicación fea detalles.

Una alternativa sería utilizar algo como esto pass-key-idiom:

class InterfaceImpl : public IInterface { 
public: 
    class Key { 
     friend std::shared_ptr<IInterface> Create(); 
     Key() {} 
    }; 
    InterfaceImpl(const Key&) {} 
}; 

std::shared_ptr<IInterface> Create() { 
    std::shared_ptr<IInterface> object = 
     std::make_shared<InterfaceImpl>(InterfaceImpl::Key()); 
    return object; 
} 
+0

¡Eso no compila tampoco me temo! – Robinson

+0

@rob: Huh, lo probé en VC10 antes de publicarlo. ¿Estás probando con código diferente? –

+0

Tal vez me perdí algo. Aquí está el código que estoy compilando (seguimiento de "respuesta" ya que el comentario no permite que el código parezca ...!) ... – Robinson

4

a la pregunta original, std :: make_shared < ...>() no crea directamente a su clase, por lo que proporciona acceso a amigo no produce ningún beneficio, como descubriste. Simplemente puede proporcionar amigo el acceso al código que no utiliza directamente el constructor protegida de la siguiente manera:

friend class std::tr1::_Ref_count_obj<TheClassManagedByTheShared_Ptr>; 

o en su caso:

friend class std::tr1::_Ref_count_obj<InterfaceImpl>; 

Esto funciona con el compilador de Microsoft en VS2010, pero parece al igual que puede ser específico del entorno, ya que no funciona con gcc en Linux. Con gcc, el espacio de nombres std :: tr1 no existe, por lo que debe ser específico para la implementación de Microsoft de la biblioteca std.

Mi entorno de trabajo normal es el compilador Intel 12.1, que parece tener un error que no comprueba el acceso en absoluto, y construye código felizmente sin ninguna declaración de amigo.

+0

Trabajó con VC2013. Incluso si mi clase fue modelada. – fmuecke

Cuestiones relacionadas