0

次のような 3 つのクラスがあるとします (これは例であるため、コンパイルされません!)。

class Base
{
public:
   Base(){}
   virtual ~Base(){}
   virtual void DoSomething() = 0;
   virtual void DoSomethingElse() = 0;
};

class Derived1
{
public:
   Derived1(){}
   virtual ~Derived1(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD1DoSomething{ ... }
};

class Derived2
{
public:
   Derived2(){}
   virtual ~Derived2(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD2DoSomething{ ... }
};

実行時まで利用できない設定に応じて、Derived1 または Derived2 のインスタンスを作成したいと考えています。

実行時まで派生型を判断できないため、次のことは悪い習慣だと思いますか?...

class X
{
public:
   ....

   void GetConfigurationValue()
   {
      ....
      // Get configuration setting, I need a "Derived1"
      b = new Derived1();

      // Now I want to call the special DoSomething for Derived1
      (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();      
   }
private:
   Base* b;
};

私は一般的に、dynamic_cast の使用法が悪いと読んでいますが、前述したように、実行時までどのタイプを作成すればよいかわかりません。助けてください!

4

7 に答える 7

6

base へのポインターへの派生へのポインターを割り当てることによって、型情報の一部を「捨てる」瞬間を遅らせてみませんか。

void GetConfigurationValue()
{
  // ...
  // Get configuration setting, I need a "Derived1"
  Derived1* d1 = new Derived1();
  b = d1;

  // Now I want to call the special DoSomething for Derived1
  d1->SpecialD1DoSomething();
}
于 2010-06-17T20:08:56.740 に答える
3

仮想関数のポイントは、適切な種類のオブジェクトを取得すると、このオブジェクトがどの派生クラスであるかを知らなくても、適切な関数を呼び出すことができるということです。仮想関数を呼び出すだけで、正しいことを実行します。

基本クラスには存在しないdynamic_cast別のものを定義する派生クラスがあり、余分なものを考慮する必要がある/したい場合にのみ必要です。

例えば:

struct Base { 
    virtual void do_something() {}
};

struct Derived : Base { 
    virtual void do_something() {} // override dosomething
    virtual void do_something_else() {} // add a new function
};

を呼び出したいだけならdo_something()、 adynamic_castは完全に不要です。たとえば、 のコレクションを持っていて、そのオブジェクトが実際にかかを気にせずに、すべてのオブジェクトをBase *呼び出すことができます。do_something()BaseDerived

がありBase *、 を呼び出したい場合は、 a を使用して、オブジェクト自体が本当に であるかどうかを判断して、do_something_else()それ呼び出すことができます。dynamic_castDerived

于 2010-06-17T20:11:26.567 に答える
2

dynamic_castの使用を避けるために考慮したい他のいくつかの事柄

効果的なC++(第3版)から-アイテム35仮想関数の代替-

  1. Non-Vitual Interface(NVI)を介した「テンプレートメソッドパターン」。パブリックメソッド「ラッパー」を使用して仮想関数をプライベート/保護する-仮想メソッドの前後に実行する他のワークフローを強制できます。
  2. 関数ポインタを介した「戦略パターン」。追加のメソッドを関数ポインタとして渡します。
  3. tr1::functionによる「戦略パターン」。2.に似ていますが、クラス全体にさまざまなオプションを提供できます
  4. 「ストラテジーパターン」クラシック。戦略をメインクラスから分離する-仮想関数を別の階層にプッシュします。
于 2010-06-17T20:49:13.093 に答える
2

dynamic_cast を使用すること自体は悪い習慣ではありません。不適切に使用するのは悪い習慣です。つまり、実際には必要のない場所で使用します。

次のように使用することも悪い習慣です。

(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();  

理由: dynamic_cast(b) が NULL を返す場合があります。

dynamic_cast を使用する場合は、b が実際に Derived1 型であり Derived2 型ではないことが保証されていないため、特に注意する必要があります。

void GenericFunction(Base* p)
{
    (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
}

void InitiallyImplementedFunction()
{
   Derived1 d1;
   GenericFunction(&d1); // OK... But not for long. 
   // Especially, if implementation of GenericFunction is in another library
   // with not source code available to even see its implementation 
   // -- just headers
}    

void SomeOtherFunctionProbablyInAnotherUnitOfCompilation()
{
   Derived2 d2;
   GenericFunction(&d2); // oops!
}

dynamic_cast が実際に成功したかどうかを確認する必要があります。キャストの前後にチェックするという 2 つの方法があります。キャストする前に、キャストしようとしているポインターが実際に RTTI を介して期待するものであるかどうかを確認できます。

if (typeid(b) == typeid(Derived1*))
{
   // in this case it's safe to call the function right 
   // away without additional checks
   dynamic_cast<Derived1*>(b)->SpecialD1DoSomething();
}
else
{
  // do something else, like try to cast to Derived2 and then call
  // Derived2::SpecialD2DoSomething() in a similar fashion
}

事後確認は、実際にはもう少し簡単です。

Derived1* d1 = dynamic_cast<Derived1*>(b);
if (d1 != NULL)
{
   d1->SpecialD1DoSomething();
}

また、C++ でのプログラミング中にタイピングを節約しようとするのは悪い習慣だと思います。C++ には、型を短くしてもまったく問題ないと思われる機能がたくさんありますが (つまり、「ここでは NULL が発生しない」と感じさせられます)、後でデバッグするのが面倒になることがわかります。;)

于 2010-06-17T20:47:05.330 に答える
1

何が問題になっていますか:

Base * b;
if( some_condition ) {
   b = new Derived1;
}
else {
   b = new Derived2;
}

if ( Derived2 * d2 = dynamic_cast <Derived2 *>( b ) ) {
    d2->SpecialD2DoSomething();
}

それとも私は何かが足りないのですか?

そして、OIは、このような質問を投稿するときに、あなた(および他の人)がクラスにA、B、Cなどの名前を付け、関数にf1()、f2()などの名前を付けることを提案できます。 。

于 2010-06-17T20:04:44.143 に答える
1

このシナリオに適合するFactory Patternという名前のパターンがあります。これにより、入力パラメーターに基づいて正しいクラスのインスタンスを返すことができます。

楽しみ!

于 2010-06-17T20:08:44.430 に答える
1

回避する 1 つの方法dynamic_castは、仮想トランポリン関数「SpecialDoSomething」を使用することです。その派生ポリモーフィック実装は、その特定の派生クラスの「SpecialDxDoSomething()」を呼び出します。これは、任意の非基本クラス名にすることができます。複数の関数を呼び出すこともできます。

于 2010-06-17T20:10:49.027 に答える