2011-08-09 9 views
5

私はdynamic_castに関する質問に返答しました。基本クラスには仮想メソッドがないため、dynamic_castは機能しませんでした。答えの1つは、仮想メソッドがないクラスから派生すると、一般にデザインが悪いということです。これは正しいです?多形性を利用しなくても、これを行う際に間違っていることはまだわかりません。仮想メソッドを持たないベースクラスの継承は悪い習慣ですか?

答えて

6

それは私たちが何を言ってるのかによって異なります。特性クラス(データなし)のための

  • 結構です(std::unary_functionが頭に浮かぶ)
  • private継承のために(空のベースから利益を得る代わりに組成物を使用最適化)これは問題ありません。

このような派生オブジェクトをこのBaseクラスで多態的に扱うと問題が発生します。あなたがこのような立場を達成したなら、それは確定コードコード臭です。

注:上記のように細心の注意を払っても、クラスをポリモーフィックに使用する機能は提供されているため、微妙なバグに晒されています。

+0

多態的な使用についての良い点がありますが、あなたのクラスがあなたの輪郭に沿って適切に定義されている場合、ユーザーはこれを回避するために余分な距離を置かないという責任があります。 –

+1

@Ken:この責任に関する問題があります。意図しない場合であっても、ユーザは多分、混乱してそのクラスを使用するかもしれません。確かに、両方のケースでそれはそうではありません。前者では、特性は非静的メソッドを持たないため、無用であり、後者の「非公開」メソッドでは、クラスおよびフレンドのメソッドに対するエラーの可能性が制限されます。それでも、それは起こるかもしれません。問題は(大部分)欠けている機能です:継承*ではなく、*委譲*がここにあります。 –

0

C++での仮想メソッドを使用しない継承は、コードの再利用に過ぎません。 多型を持たない継承は考えられません。

2

コードの再利用のために、クラスからの派生は常に有効なオプションです。

場合によっては、多態性の動作を探しているわけではありません。それは問題ありません。そのオプションがある理由があります。しかし、その場合、プライベート継承を使用することを検討してください。クラスが多型であることが意図されていない場合は、誰でもそれを多態的に使用しようとする理由はありません。

struct some_policy 
{ 
    // Some non-virtual interface here 
protected: 
    ~some_policy() { ... } 

private: 
    // Some state here 
}; 

struct some_class : some_policy, some_other_policy { ... }; 

別のオクラホマの例、テンプレートにコードの膨張を避けるために:ここで

+2

私はコードの再利用のために継承* just *を使用しません。コンポジションはタスクに適しています*さらに*優れており、継承を使用すると基本クラスにもっと強く結びつくだけでなく、微妙な(変換/隠れ)バグにさらされます。 –

+0

@Matthieu:議論の余地はありますが、有効な議論です。それは間違いなく注意を必要とし、ユーザーの責任を負う必要があることは間違いありません。私は、構成が*通常は*良いことに同意しますが、私的継承は(まだ正しく使用するのは難しいですが)代替案です。 –

+2

私は、最適化(EBO)または仮想メソッドをオーバーライドする(ここでは関係ありません)という2つのケースで、私的継承を実行可能と判断しました。他のすべては怠惰/利便性の問題です。私は継承/構成の議論についてかなり心配していたと言いましたか? –

0

は、(保護されたデストラクタに注意してください)政策に行動を考慮し、OK例です。保護デストラクタに注意してください。

struct base_vector 
{ 
    // Put everything which doesn't depend 
    // on a template parameter here 

protected: 
    ~base_vector() { ... } 
}; 

template <typename T> 
struct vector : base_vector 
{ ... }; 

もう1つの例は、CRTPと呼ばれます。保護デストラクタに注意してください。

template <typename Base> 
struct some_concept 
{ 
    void do_something { static_cast<Base*>(this)->do_some_other_thing(); } 

protected: 
    ~some_concept() { ... } 
}; 

struct some_class : some_concept<some_class> { ... }; 

もう1つの例は、空の基本最適化です。実際には継承自体ではありません。なぜなら、プライベートメンバーとして動作する基本クラスに対して、コンパイラがsome_classに空き領域を予約できないようにするのは、より面倒なことです。

template <typename T> 
struct some_state_which_can_be_empty { ... }; 

template <typename T> 
struct some_class : private some_state_which_can_be_empty<T> { ... }; 

経験則として、継承するクラスには、仮想デストラクタまたは保護されたデストラクタが必要です。

+0

私は、公開に関する継承について知っています。 –

+0

@Alexandre C:ポリシーとコード拡張の両方の例では、代わりに構成を使用できます(EBOが不要な場合)。 CRTPの例は、伝統的な意味での多態性(派生クラスに応じて基本クラス)を許さないため、効果的です。 –

+0

@Matthieu:コードが肥大化し、ポリシーの例には通常、いくつかの些細なパブリックインターフェイスがあります。あなたがメンバーになれば、手でラッパーを書く必要があります。手間のかかるジェネリックコードとC++ 03(C++ 0xと完璧な転送で簡単) –

0

C++標準ライブラリの一部のクラスには、保護されたメンバー(派生クラスにとって意味のある)がありますが、仮想メンバ関数はありません。つまり、彼らは仮想を持たずに、派生のために設計されています。これは、仮想を持たないクラスから派生することは、一般的に悪い設計でなければならないことを証明しています。

乾杯&hth。、

関連する問題