0

添付のようなものがあります。私は基本的に、仮想を使用せずに、またはコードの重複を最小限に抑えて、そのメンバーから Func() を呼び出したい Doer クラスを持っています。また、ブーストもオプションではありません。例がわかりにくいかもしれませんが、ご理解いただければ幸いです。B

class Base { // a bunch of shared base functionality. Cannot be instantiated by itself  }

class D1 : public Base
{
   void Func();
}

class D2 : public Base
{
   void Func();
}

//----

class Doer
{
   Doer(Base* b) : base(b) { } 

   void DoIt()
   {
      base->Func();
   }

   Base* base;
}
4

4 に答える 4

3

さて、あなたはDoerテンプレートを作ることができます:

template<class T>
class Doer
{
public:
   Doer(T* b) : base(b) { } 

   void DoIt()
   {
      base->Func();
   }

private:
   T* base;
};

しかし、このために、代わりににを追加virtual void Func()Baseます。

Funcどちらの場合も、おそらく公開したいと思うでしょう:-)

于 2012-04-26T18:28:02.833 に答える
0

ミックスインが使えます!それらは最適化には適していますが(多くのインライン化の機会があり、仮想メソッドの呼び出しはありません)、推論するのが少し難しい場合があります。ミックスインで実装された例は次のとおりです。

template<class Base> class Doer : Base {
public:
    Doer() {}
    void DoIt() {
        this->Func();
    }
};

class D1 {
public:
    void Func() {
        cout<<"Hello from D1"<<endl;
    }
};

class D2 {
public:
    void Func() {
        cout<<"Hello from D2"<<endl;
    }
};

DoerはBaseクラスのインスタンスと同じであるため、これを使用する方法は少し異なります。次のプログラム:

Doer<D1> *d1 = new Doer<D1>();
Doer<D2> *d2 = new Doer<D2>();
d1->DoIt();
d2->DoIt();

出力を生成します:

D1からこんにちは

D2からこんにちは

これには、D1とD2が「Func」メソッドの実装を強制されないという明らかな欠点があります。忘れると、「メソッドが見つかりません」ではなく、非常に便利なC++テンプレートのインスタンス化エラーが発生します。テンプレートを頻繁に使用する場合は、g ++よりもはるかに役立つコンパイラエラーが発生するため、Clangは最適な選択肢です。もう1つの欠点は、コンストラクターにあります。Doerはデフォルトのコンストラクターを定義しますが、D1のコンストラクターを公開しません。C ++ 11ではコンストラクターの継承が可能であるため、この問題はコンパイラーフラグを使用して回避できます。

于 2012-04-26T19:59:59.707 に答える
0

このアプローチはどうですか:

class Base { // a bunch of shared base functionality. Cannot be instantiated by itself  
   ~Base() { //stuff }
   void Func();
}

class D1 : public Base
{
   void Func();
}

class D2 : public Base
{
   void Func();
}

//----

class Doer
{
   Doer(Base* b) : base(b) { } 

   void DoIt()
   {
      base->Func();
   }

   Base* base;
}

Func() は仮想ではなく、子によってオーバーロードされているため、vtable や発生したパフォーマンス ペナルティはありません。

また、基本クラスでデストラクタを呼び出す必要がありますが、それを仮想と宣言すると vtable が強制されますか?

誰でも明確にできますか?

ありがとう

于 2012-04-26T18:51:52.523 に答える
0

実際、Doer クラス全体をパラメータ化する必要はありません。これは問題なく機能します(ccurtsingerが提案していたものに近い):

class Base {
public:
    void Func() {};
};

class B1 {
public:
    void Func() { cout << "in B1::Func" << endl;}
};

class B2 {
public:
    void Func() { cout << "in B2::Func" << endl;}
};

class Doer {
public:
    template <class B> void Do(B *pb) {pb->Func();}
};

int main() {
    B1 b1;
    B2 b2;
    Doer d;
    d.Do<B1>(&b1);
    d.Do<B2>(&b2);

    return 0;
}

しかし、実際にはもっと大きな問題があります。最終的に使用したと言ったコードから、コンパイル時に、どの派生クラスオブジェクトを扱っているかを正確に知っているように見えるため、次のようなコードを作成します。

for(auto i = begin(B1_container); i != end(B1_container); ++i) {
    i->Func();
}
for(auto j = begin(B2_container); j != end(B2_container); ++j) {
    j->Func();
}

トリックを行う必要があります。

私が言いたいのは、ここで B1-s を使用し、そこに B2-s を使用していることを前もって知っていて、Func() の呼び出しに追加コストがかからないか、またはどちらを処理する必要があるかがわからないということです。 next を使用して、それが何らかのタイプのトレイトの動的なタイプであるかどうかを確認する必要があります。これは「if」であるため、分岐が発生し、予測ミスとオーバーヘッドが発生します。関数呼び出しのコストを追加していないことに注意してください。これは、どちらの場合にもあります。

于 2012-04-27T12:51:03.353 に答える