2016-07-21 2 views
0

Hier ist das Problem:Wie initialisiert man das Member in der abstrakten Klasse mit std :: function, in der das reine virtuelle Member aufgerufen wird?

Ich habe eine Klasse Object, dessen Konstruktor ein std::function wie dies akzeptiert genannt:

#include <functional> 
#include <iostream> 
#include <memory> 
#include <string> 

class Object { 
public: 
    Object(std::function<void(int param)> f) : func(f) {} 
    ~Object() { func(0); } 
private: 
    std::function<void(int param)> func; 
}; 

Dann eine abstrakte Basisklasse und mehrere abgeleitete Klassen wie folgt aus:

class AbstractBase { 
public: 
    AbstractBase() { 
    // How to initialize object. 
    } 
    virtual std::string toString() const = 0; 

private: 
    Object object; 
}; 

class Derived1 : public AbstractBase { 
public: 
    std::string toString() const override { return "Derived1"; } 
} 

class Derived2 : public AbstractBase { 
public: 
    std::string toString() const override { return "Derived2"; } 
} 

Ich versuche, die object in AbstractBase wie folgt zu initialisieren:

AbstractBase() 
    : object([this](int param) { 
     // do something 
     std::cout << toString() << std::endl; 
     // do something 
     }) {} 

Es kompiliert erfolgreich, aber wirft „rein virtuelle Methode namens“ wenn AbstractBase gelöscht. Also, wie kann ich object in der AbstractBase initialisieren und sicherstellen, dass toString von der abgeleiteten Klasse in der std::function aufgerufen wird?

+0

Bitte geben Sie eine [MCVE]. Versuchen Sie, die 'Objekt'-Funktion auf' this' in '~ AbstractBase()' zu verwenden? – Barry

+0

Denken Sie darüber nach, wenn der Destruktor 'AbstractBase' ausgeführt wird. –

+0

Aktualisierte die Frage. Das gewünschte Verhalten ist, dass "Object" verwendet wird, um einige Informationen von "Derived1" und "Derived2" zu erfassen, und wenn "Object" zerstört wird, werden die gesammelten Informationen gespeichert (oder in diesem Fall auf stdout gedruckt). –

Antwort

2

Das Problem hier ist die Reihenfolge der Destruktoren Aufrufe. Hier ist ein einfaches Beispiel:

#include <functional> 
#include <iostream> 
#include <memory> 
#include <string> 
using namespace std; 

class Object { 
public: 
    Object(){ cout<<"O\n"; } 
~Object() { cout<<"~O\n"; } 
}; 

class AbstractBase { 
public: 
    AbstractBase(){ cout<<"A\n"; } 
~AbstractBase() { cout<<"~A\n"; } 

private: 
    Object object; 
}; 

class Derived1 : public AbstractBase { 
public: 
Derived1() : AbstractBase() 
{ cout<<"1\n"; } 
~Derived1() { cout<<"~1\n"; } 
}; 

class Derived2 : public AbstractBase { 
public: 
Derived2() : AbstractBase() 
{ cout<<"2\n"; } 
~Derived2() { cout<<"~2\n"; } 
}; 

int main() { 
    Derived1 d1; 
    Derived2 d2; 

    return 0; 
} 

Und die Ausgabe:

O 
A 
1 
O 
A 
2 
~2 
~A 
~O 
~1 
~A 
~O 

Wie Sie Objekte Destruktoren sehen können, werden nach Abgeleitet genannt * Destruktoren so, im Code, Objekt versucht, bereits zu nennen zerstörte abgeleitete * Methode.

0

Der C++ - Standard verbietet den Aufruf einer rein virtuellen Methode von einem Konstruktor oder einem Destruktor.

Wenn Sie toString() von ~AbstractBase() direkt aufrufen würden, würden Sie fast sicher einen Compilerfehler bekommen.

Ihre Verwendung eines Lambda ist im Grunde "schleichen" das verbotene Verhalten hinter dem Compiler.

Stattdessen müssen Sie die Verantwortung bewegen, bis zu Derived - betrachten dies so etwas wie:

struct Object { 
    Object(decltype(func) f) : func(f) {}; 
    ~Object() { func(); } 
    private: 
    std::function<void()> func; //removed param since it's unused in your example 
}; 

struct AbstractBase { 
    A() = delete; 
    protected: 
    A(Object&& o) : obj(o) {} //we'll be using an rvalue in this example, be mindful of object lifetime in other usages 
    private: 
    Object& o; 
}; 

struct Derived : AbstractBase { 
    Derived() : AbstractBase(Object{[this]() { writestr(); }}) {} 
    private: 
    void writestr() { std::cout << "yes"; } 
}; 

int main() { 
    Derived d; 
    return 0; 
} 
Verwandte Themen