2016-11-19 2 views
1

Wie Sie wissen, ist es nicht möglich, das Paar std :: enable_shared_from_this und shared_from_this() aus dem Konstruktor eines Objekts zu verwenden, da ein shared_pointer, der die Klasse enthält, noch nicht existiert. Allerdings würde mir diese Funktionalität wirklich gefallen. Ich habe mein eigenes System versucht und es scheint OK zu funktionieren.Verwenden von shared_from_this() im Konstruktor

namespace kp 
{  

template <class T> 
void construct_deleter(T *t) 
{ 
    if(!t->_construct_pself) 
    { 
    t->~T(); 
    } 

    free(t); 
} 

template <class T, typename... Params> 
std::shared_ptr<T> make_shared(Params&&... args) 
{ 
    std::shared_ptr<T> rtn; 
    T *t = (T *)calloc(1, sizeof(T)); 
    t->_construct_pself = &rtn; 
    rtn.reset(t, construct_deleter<T>); 
    t = new(t) T(std::forward<Params>(args)...); 
    t->_construct_pself = NULL; 
    t->_construct_self = rtn; 

    return rtn; 
} 

template <class T> 
class enable_shared_from_this 
{ 
public: 
    std::shared_ptr<T> *_construct_pself; 
    std::weak_ptr<T> _construct_self; 

    std::shared_ptr<T> shared_from_this() 
    { 
    if(_construct_pself) 
    { 
     return *_construct_pself; 
    } 
    else 
    { 
     return _construct_self.lock(); 
    } 
    } 
}; 

} 

Kann jemand irgendwelche Fehler in dieser Logik entdecken? Ich benutze im Grunde die Platzierung neu, um einen Zeiger auf die shared_ptr innerhalb der Klasse zuzuweisen, bevor der Konstruktor aufruft.

Wie es aussieht kann ich es als so verwenden:

std::shared_ptr<Employee> emp = kp::make_shared<Employee>("Karsten", 30); 

und in dem Employee-Konstruktor:

Employee::Employee(std::string name, int age) 
{ 
    Dept::addEmployee(shared_from_this()); 
} 

Bevor ich dies zu einer relativ großen Code-Basis verpflichten, würde ich wirklich zu schätzen einige Ideen oder Feedback von euch.

Danke!

+0

Sie können diese nur über Ihre benutzerdefinierte 'make_shared' erstellen; Wenn Sie versuchen, eine andere Form der Initialisierung zu verwenden, wird es zur Laufzeit fehlschlagen. Verwenden Sie einfach das Standardmuster der Bereitstellung einer statischen 'create()' -Funktion, die eine Instanz erstellt, und führt dann alle Operationen aus, die 'shared_from_this()' erfordern, bevor Sie sie zurückgeben. –

+0

Es tut mir leid, aber es gibt keine Möglichkeit, dass diese Art von Code in meinem Team überprüft wird. Können Sie nicht ein einfacheres und selbstdokumentierendes Design entwickeln? Was ist das Problem, das Sie wirklich zu lösen versuchen, und warum erfüllen konventionelle Ansätze dieses Ziel nicht? –

Antwort

1

Ich denke, es gibt ein semantisches Problem bei der Verwendung von shared_from_this() im Konstruktor. Das Problem ist, wenn eine Ausnahme ausgelöst wird, gibt es kein gültiges Objekt, aber Sie haben bereits einen gemeinsamen Zeiger darauf eingerichtet. z.B .:

Employee::Employee(std::string name, int age) 
{ 
    Dept::addEmployee(shared_from_this()); 
    if (...) throw std::runtime_error("..."); 
} 

Jetzt Dept einen Zeiger auf dieses Objekt haben, die nicht erfolgreich erstellt wurde.

+0

Ah, aber wenn wir den addEmployee nur am Ende des Konstruktors ausführen und sicherstellen, dass removeEmployee am Anfang des Destruktors ausgeführt wird, dann würde dies auch für die Vererbung "class Manager: public Employee" funktionieren. Es würde dann auch sicherstellen, dass ein unvollständiger Mitarbeiter/Manager niemals in der Abteilung nach dem Aufruf von make_shared wäre. –

+0

Ich habe gerade bemerkt, dass Ihr Code ein wenig unerwarteter wirkt: Wenn eine Ausnahme im C'tor von Employee ausgelöst wird, wird ein D'or-Anruf erzwungen. Aber der D'tor sollte nicht gerufen werden, wenn ein C'tor wirft. – Gene

+0

Sie haben Recht. Während ich immer noch den Speicher freigeben muss, hätte ich den Aufruf des Destruktors wirklich in etwas wie "if (! T -> _ construct_self)" oder etwas ein bisschen Schöneres umgeben. Ich werde das jetzt reparieren. Vielen Dank. –

Verwandte Themen