2012-03-23 6 views
3

概要ファイルをロードするための複数の方法:コンストラクタC++の設計パターン:

を経由して別のファイルをロードするための標準C++の設計パターンの検索では、私はすべての派生クラスで使用されるいくつかの機能を持つBaseクラスを持っています(例えば、Derived_A,Derived_B)。主な相違点は、関数をオーバーライドするとDerived_Bがコンストラクタによってデータファイルをロードするために使用されることです(loadもコンストラクタの外で明示的に呼び出すことができます)。

私はこのことから、予期せぬ問題に遭遇した:コンストラクタによって呼び出さload機能はBase型としてクラスを扱いますが、私は、デフォルトのコンストラクタを使用して明示的にload関数を呼び出すとき、仮想関数テーブルは、意図を許可load関数が呼び出されます。

これは古典的な問題のようなにおいがしますが、私はそれを行う方法を理解できません(弱い型定義のために、私は最近Pythonでプログラミングしていました。

同じように、Base::loadは純粋な仮想/抽象であることが本当に欲しいです(派生クラスのみがインスタンス化されます)。しかし、それはコンパイルされません(コンパイラは純粋な仮想関数が呼び出されると見ているので、私は信じています)。

お手伝いできますか?

出力:

ロード/コンストラクタワット:
ベース::負荷file_Bロード/ファンクション・ポスト建設ワット
ベース::負荷file_A:
Derived_A ::負荷file_A
Derived_B ::ロードファイル_B

コード:

#include <iostream> 
#include <string> 

class Base 
{ 
public: 
    Base() {} 
    Base(std::string x) 
    { 
    load(x); 
    } 
    virtual void load(std::string x) 
    { 
    std::cout << "\tBase::load " << x << std::endl; 
    } 
}; 

class Derived_A : public Base 
{ 
public: 
    Derived_A() {} 
    Derived_A(std::string x): Base(x) {} 
    void virtual load(std::string x) 
    { 
    std::cout << "\tDerived_A::load " << x << std::endl; 
    } 
}; 

class Derived_B : public Base 
{ 
public: 
    Derived_B() {} 
    Derived_B(std::string x): Base(x) {} 
    void virtual load(std::string x) 
    { 
    std::cout << "\tDerived_B::load " << x << std::endl; 
    } 
}; 

int main() 
{ 
    // simpler code, but it doesn't behave as I hoped 
    std::cout << "Loading w/ constructor:" << std::endl; 
    Base*der_a = new Derived_A(std::string("file_A")); 
    Base*der_b = new Derived_B(std::string("file_B")); 

    // this is what I want to do 
    std::cout << "Loading w/ function post construction:" << std::endl; 
    der_a = new Derived_A; 
    der_a->load(std::string("file_A")); 
    der_b = new Derived_B; 
    der_b->load(std::string("file_B")); 
    return 0; 
} 

答えて

2

ご覧の行動はよくC++で定義されている - あなたはBase::Base(std::string)からload(std::string)を呼び出したときにクラスが完全に構築されていないため、このシナリオでは、単に便利ではありません。

2つの当面のアプローチがあります:あなたは負荷を呼び出して(そしておそらく同様の文字列に上保持している)コンテナタイプを使用することができ

が。これは、インスタンスを保持する必要がある場合(たとえば、特殊なエラー情報を持つ可能性がある場合など)、より現実的かもしれません。使用の際に

class Loader 
{ 
public: 
    Loader(Base* const p, const std::string& location) : d_base(p) 
    { 
     this->d_base->load(location); 
    } 

private: 
    std::unique_ptr<Base>d_base; 
private: 
    Loader(const Loader&) = delete; 
    Loader& operator=(const Loader&) = delete; 
}; 

std::cout << "Loading w/ Loader:\n"; 
Loader l_der_a(new Derived_A, "file_A"); 
Loader l_der_b(new Derived_B, "file_B"); 

B

また、ヘルパー関数を使用して、それに近づくことができます:

使用で
class Base { 
public: 
    template<typename T> 
    static void Load(const std::string& x) 
    { 
     T().load(x); 
    } 

    Base() 
    { 
    } 

    Base(std::string x) 
    { 
     /* load(x); << see Load(const std::string&) */ 
    } 

    virtual ~Base() 
    { 
    } 

    virtual void load(std::string x) = 0; 
}; 

std::cout << "Loading w/ Base::Load<T>():\n"; 
Derived_A::Load<Derived_A>("file_A"); 
Derived_B::Load<Derived_B>("file_B"); 

さらに、いくつかのアプローチやバリエーションがあります。デザインに最も適したものによって異なります。 C++では、確かにオプションがあります。

+1

私は 'loader'パターンを使ってしまいました。どうもありがとう! – user

2

"Named Constructor Idiom"を検索できます。

+0

+1良い点、これはPythonが奨励するものです。各サブクラスの名前付きコンストラクタを記述する必要があるかどうかは分かりますか?あるいは、重複したコードを防ぐために一度書く方法があります(常に 'load'を呼び出してから、他の関数を呼び出すこともあります)。 – user

+0

あなたはアクセスファイルを要求する権限がありません(リンクを開こうとするとエラーが発生します) – GameDeveloper

関連する問題