2016-07-21 2 views
0

ここでは問題です: 純粋な仮想メンバが呼び出されるstd :: functionを持つ抽象クラスでメンバを初期化する方法は?

#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; 
}; 

次に、このような抽象基底クラスと、いくつかの派生クラス:

私は、そのコンストラクタObjectというクラスは、このようなstd::functionを受け付けてい

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"; } 
} 

objectAbstractBaseに初期化しようとしています:

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

それは正常にコンパイルが、AbstractBaseが削除されたとき「と呼ばれる純粋仮想メソッド」を発生させます。だからobjectAbstractBaseに初期化し、派生クラスのtoStringstd::functionで呼び出されていることを確認しますか?

+0

は[MCVE]を提供してください。 '〜' AbstractBase() 'の' this'に 'object'関数を使用しようとしていますか? – Barry

+0

'AbstractBase'デストラクタがいつ実行されるかを考えてください。 –

+0

質問が更新されました。望ましい動作は、 'Object'が' Derived1'と 'Derived2'からいくつかの情報を取得するのに使用され、' Object'が破壊されると、収集された情報が保存されます(この場合はstdoutに出力されます)。 –

答えて

2

ここでの問題は、デストラクタ呼び出しの順序です。あなたがオブジェクトのデストラクタは、派生*デストラクタの後に呼び出され、そう、あなたのコードでは、オブジェクトがすでに呼び出ししようとしている見ることができるように

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

#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; 
} 

そして出力:ここ は簡単な例です。破壊されたDerived *メソッド

0

C++標準では、コンストラクタまたはデストラクタから純粋仮想メソッドを呼び出すことを禁じています。

toString()~AbstractBase()から直接呼び出すと、ほぼ確実にコンパイラエラーが発生します。

ラムダを使用すると、基本的には、許可されていない動作がコンパイラを通過するのを「忍び込ませる」ことができます。

は代わりに、あなたはDerivedに責任を上に移動する必要がある - このような何かを考えてみます。

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; 
} 
関連する問題