2017-11-15 16 views
1

私のアプリケーションでは、.csvファイルからデータを読み取る複数のリーダーがあります。私は今、それらのメソッドをgetData(std::string filename)という共通点を持つ読者のための親クラスを作成して、それらを構造化したいと思います。私は基本クラスに仮想メソッドを実装することでそれをやりたかったのです。ファイル名はコンストラクタによって渡されるべきです。親クラスの仮想関数で子クラスのメソッドが呼び出されない

メイン

int main() 
{ 
    std::string filename = "file.csv"; 
    ChildReader1 reader = new ChildReader1(filename); 
} 

ChildReader1.h

class ChildReader1: public ParentReader 
    { 
    public: 
     ChildReader1(std::string filename) 
      : ParentReader(filename) 
     { 

     }; 
     void getData(std::string filename) 
     { 
      //get the data here 
     } 
    }; 

ParentReader.h

class ParentReader 
    { 
    public: 
     ParentReader() {}; 
     ParentReader(std::string filename) 
     { 
      getData(filename); 
     }; 
     ~ParentReader() {}; 

     virtual void getData(std::string filename) {}; 
    }; 

現時点では、ファイル名がParentReaderに渡されたが、のgetData(ファイル名)は、仮想を開きますChildReader 1の実際のメソッドの代わりにParentReaderのメソッドを使用します。これをどのように解決できますか?

+0

仮想メソッドは、ベース(親)クラスの構築中に呼び出されません。派生したものはまだ準備ができていないからです。 –

+0

さて、基本クラスのサブクラスにあるメソッドをどのように呼び出すことができますか? –

+0

子メソッドを呼び出す場合は、子オブジェクトを作成する必要があります。 – stark

答えて

0

派生クラスはまだ準備ができていないため、基本クラスコンストラクターの派生クラスに対して仮想メソッドを呼び出すことはできません。

一つの解決策は、ChildReader1に工場出荷時の機能を持つことです。

class ParentReader { 
public: 
    ParentReader(){}; 
    void initialize(const std::string& filename){ 
    getData(filename); 
    }; 
    virtual ~ParentReader(){}; 
    virtual void getData(const std::string& /*filename*/) { 
    }; 
}; 

class ChildReader1 : public ParentReader { 
private: 
    ChildReader1(){} 
public: 
    void getData(const std::string& /*filename*/) override { 
    // get the data here 
    } 
    static std::unique_ptr<ChildReader1> create(const std::string &filename) { 
    auto reader = std::unique_ptr<ChildReader1>(new ChildReader1); 
    reader->initialize(filename); 
    return reader; 
    } 
}; 

int main() { 
    std::string filename = "file.csv"; 
    auto reader = ChildReader1::create(filename); 
} 

工場の機能が完全に形成されたオブジェクトを作成してから復帰する前に仮想関数を呼び出すことができます。オブジェクトのコンストラクタをプライベートにして、呼び出し元にファクトリ関数を使用させることができます。

派生クラス間のコードの重複を避けるために、中間のCRTPクラスを導入することができます。

+0

@ Jarod42はより良く、より一般的です –

+0

また、親がすべてのデータを保持し、子がそれをロードするだけであれば、それを関数として親コンストラクタに渡すことができます。 –

+0

@AA Jarod42'sは良いです。このようにすることの1つの問題は、呼び出し元がコンストラクターを直接呼び出して、アンロードされたリーダーで終了することを妨げるものは何もありません。 –

1

仮想メソッドは、ベース(親)クラスの構築中に呼び出されません。派生したものはまだ準備ができていないからです。仮想関数(10.3)を含む

12.7建設破壊[class.cdtor]#4 ISO/IEC N3797

メンバ関数は、建設または破壊(12.6.2)の間に呼ぶことができます。仮想関数が、クラスの非静的データメンバの構築または破棄を含む、コンストラクタまたはデストラクタから直接的または間接的に呼び出され、呼び出しが適用されるオブジェクトは、構築中のオブジェクト(xと呼ばれます)です破壊された場合、コンストラクタまたはデストラクタのクラスの最後のオーバーライドであり、より派生したクラスではオーバーライドされません。仮想関数呼び出しが明示的なクラスメンバアクセス(5.2.5)を使用し、オブジェクト式がxの完全なオブジェクトまたはそのオブジェクトの基本クラスのサブオブジェクトの1つを参照するが、xまたは基本クラスのサブオブジェクトの1つを参照しない場合、その動作は未定義である。

2

あなたは工場であなたの問題を解決する可能性があります:他の人が言ったように

class ParentReader 
{ 
public: 
    virtual ~ParentReader() = default 
    virtual void getData(const std::string& filename) = 0; 
}; 

template <typename T, typename ... Ts> 
std::unique_ptr<T> MakeReader(const std::string& filename, Ts&&... args) 
{ 
    static_assert(std::is_base_of<ParentReader, T>::value, "!"); 
    auto res = std::make_unique<T>(std::forward<Ts>(args)...); 
    res->getData(filename); 
    return res; 
} 
0

、あなたは、コンストラクタで仮想関数を呼び出すべきではありません。あなたが考えてみれば理由は極めて単純で何が起こっている:ChildConstructorの

コール - > ParentConstructorの呼び出し - 親の>作成 - >のgetDataの呼び出し - のgetDataの呼び出しでは子供

の>作成既存のオブジェクトだけがParentであるため、おそらくChildのgetDataを呼び出すことはできません。

関連する問題