2

最近、厄介な問題が発生しましたが、自分自身の回避策に満足していません。基本クラスへのポインターのベクトルを維持するプログラムがあり、そこにあらゆる種類の子オブジェクトポインターを格納しています。現在、各子クラスには独自のメソッドがあり、メインプログラムは、オブジェクトのタイプに応じてこれらのメソッドを呼び出す場合と呼び出さない場合があります(ただし、これらはすべて基本クラスの共通メソッドを頻繁に使用するため、継承が正当化されます)。

クラスタイプをチェックする(そしてメソッドを呼び出すかどうか)ための「オブジェクト識別子」があると便利だと思いました。これはまだあまり美しくありませんが、これは主な不便ではありません。主な不便な点は、基本クラスのポインターを使用して派生クラスのメソッドを実際に呼び出すことができるようにする場合(または、ポインターをポインター配列に格納する場合でも)、派生メソッドを基本で仮想として宣言する必要があることです。クラス。

C ++コーディングの観点からは理にかなっていますが、私の場合(開発の観点から)は実用的ではありません。さまざまなファイルでさまざまな子クラスを作成することを計画しているためです。仮想メソッドを追加するために、毎回基本クラスを微調整/維持したくない!

これを行う方法?基本的に、私が求めているのは(おそらく)Objective-C NSArraysのようなものを実装する方法です-メソッドを実装していないオブジェクトにメッセージを送信しても、何も起こりません。

よろしく

4

4 に答える 4

1

これの代わりに:

// variant A: declare everything in the base class
void DoStuff_A(Base* b) {
  if (b->TypeId() == DERIVED_1) 
      b->DoDerived1Stuff();
  else if if (b->TypeId() == DERIVED_2) 
      b->DoDerived12Stuff();
}

またはこれ:

// variant B: declare nothing in the base class
void DoStuff_B(Base* b) {
  if (b->TypeId() == DERIVED_1) 
      (dynamic_cast<Derived1*>(b))->DoDerived1Stuff();
  else if if (b->TypeId() == DERIVED_2) 
      (dynamic_cast<Derived2*>(b))->DoDerived12Stuff();
}

これを行う:

// variant C: declare the right thing in the base class
b->DoStuff();

実行する必要があるものごとに、ベースに単一の仮想関数があることに注意してください。

バリアント A または B の方がバリアント C よりも快適である場合は、設計を停止して再考してください。コンポーネントの結合が強すぎると、最終的に裏目に出てしまいます。

おそらくさまざまな人によって作成された、さまざまなファイルにさまざまな子クラスを作成することを計画しています。仮想メソッドを追加するために、毎回基本クラスを調整/維持したくありません!

DoStuff派生クラスが追加されるたびに微調整することは問題ありBaseませんが、微調整は禁物です。どういうことなんですか?

あなたのデザインが A、B、C のいずれのパターンにも当てはまらない場合は、あなたが持っているものを見せてください。

于 2013-03-10T11:33:48.393 に答える
1

これは、ある種のイントロスペクション機能とメタ オブジェクト システムを追加することで解決できます。この講演では、C++ におけるメタデータとリフレクション — Jeff Tuckerが、c++ のテンプレート メタ プログラミングを使用してこれを行う方法を示しています。

わざわざ自分で実装したくない場合は、Qtメタ オブジェクト システムなどの既存のものを使用する方が簡単です。このソリューションは、メタ オブジェクト コンパイラの制限により、多重継承では機能しないことに注意してください: QObject Multiple Inheritance

これをインストールすると、メソッドの存在を照会して呼び出すことができます。これを手動で行うのは非常に面倒なので、このようなメソッドを呼び出す最も簡単な方法は、シグナルとスロットのメカニズムを使用することです。

かなり似ているGObjectもあり、他にもあります。

于 2013-03-10T09:49:20.530 に答える
1

C++で記述したことはできますが、関数を使用することはできません。ところで、それは恐ろしいことですが、正当なアプローチである場合もあると思います。

これを行う最初の方法:

基本クラスに共通の署名を持つ便利な関数の文字列のような署名を持つ関数を定義し、boost::variant parseMessage(std::string, std::vector<boost::variant>);ファンクターを取る基本クラスにメッセージルックアップテーブルを含めます。各クラス コンストラクターでメッセージをメッセージ テーブルに追加し、parseMessage 関数が各メッセージをクラスの適切な関数に分割します。

醜くて遅いですが、うまくいくはずです。

これを行う2番目の方法:

階層のさらに下に仮想関数を定義するため、追加するint foo(bar*);場合は、最初にそれを仮想として定義するクラスを追加し、次に定義するすべてのクラスがそれint foo(bar*);から継承するようにします。dynamic_cast次に、 を呼び出す前に、参照しているポインタがこのクラスから継承されていることを確認するために使用できますint foo(bar*);。クラスを追加するこれらのインターフェイスは純粋な仮想である可能性があるため、多重継承を使用してさまざまなポイントに混在させることができますが、それには独自の問題がある可能性があります。

これは最初の方法よりも柔軟性が低く、関数を実装するクラスを相互にリンクする必要があります。ああ、それはまだ醜いです。

しかし、ほとんどの場合、Objective-C コードではなく、C++ コードのように C++ コードを書いてみることをお勧めします。

于 2013-03-10T08:37:22.673 に答える
0

あなたがそうplanning to create many different children classes in different files, perhaps made by different peopleで、すべての子クラスのメインコードを変更したくないと思います。次に、基本クラスで行う必要があるのは、いくつかの(多くない)仮想関数を(空の実装で)定義することだと思いますが、これらの関数は、「AfterInseart」のように呼び出されるロジックで時間をマークするために使用する必要がありますまたは「BeforeSorting」など
通常、ロジック内で派生クラスに独自のロジックを実行させたい場所はあまりありません。

于 2013-03-10T10:43:36.547 に答える