2010-08-22 9 views
5

Data un'interfaccia astratta e un'implementazione derivata da tale interfaccia, in cui i costruttori sono protetti (creazione di questi oggetti che sono disponibili solo da un factory di classe - per implementare un modello DI), come Faccio uso di make_shared nella funzione di fabbrica?Uso di make_shared con un costruttore protetto + interfaccia astratta

Ad esempio:

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, ovviamente, non può accedere il costruttore protetto in InterfaceImpl, o addirittura in IInterface, dandomi il seguente errore


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

Quindi leggere qui (domanda: How to make boost::make_shared a friend of my class), Ho provato a mettere quanto segue nella classe di implementazione:


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

Ancora non verrebbe compilato. Quindi ne inserisco un altro nella classe IInterface. Ancora nessuna gioia. Cosa ho fatto di sbagliato qui?

EDIT: il file sorgente completo utilizzato per la compilazione, con "amico" ...

#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

Immagino che sia VC10? GCC btw non ha problemi finché tu fai amicizia con 'make_shared()'. –

+0

È VS2010, che in realtà fornisce un avviso (erroneamente - dettagliato qui: http://connect.microsoft.com/VisualStudio/feedback/dettagli/321.690/c-VC9-falso-allarme-c4396-per-valido codice). – Robinson

risposta

4

Con VC10 la soluzione si è collegato al non funziona - la costruzione della istanza di InterfaceImpl non accade in make_shared, ma in un tipo interno in std::tr1::_Ref_count_obj<Ty>::_Ref_count_obj(void).

avevo appena rendere la funzione Create() un friend nel tuo caso e non 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 utilizzare un make_shared() implementazione personalizzata che in realtà si può fare amicizia senza fare affidamento sul brutto implementazione dettagli.

Un'alternativa sarebbe quella di utilizzare qualcosa di simile 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

Non ho compilato neanche io ho paura! – Robinson

+0

@rob: Huh, l'ho testato su VC10 prima di postare. Stai provando con un codice diverso? –

+0

Forse mi sono perso qualcosa. Ecco il codice che sto compilando (segui "risposta" in quanto il commento non consente il codice sembra ...!) ... – Robinson

4

alla domanda iniziale, std :: make_shared < ...>() non istanziare direttamente la classe, in modo da fornire l'accesso amico non ha alcun beneficio, come hai trovato. Si può semplicemente fornire amico l'accesso al codice che fa uso direttamente il vostro costruttore protetta come segue:

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

o nel tuo caso:

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

Questo funziona con il compilatore Microsoft in VS2010, ma sembra come potrebbe essere specifico per l'ambiente in quanto non funziona con gcc su Linux. Con gcc il namespace std :: tr1 non esiste, quindi deve essere specifico all'implementazione Microsoft della libreria std.

Il mio ambiente di lavoro normale è il compilatore Intel 12.1, che sembra avere un bug che non controlla affatto l'accesso e crea felicemente il codice senza alcuna dichiarazione di amicizia.

+0

Ha funzionato con VC2013. Anche se la mia classe fosse basata su modelli. – fmuecke

Problemi correlati