0

プログラミングのミスを避けるために、特定の種類のオブジェクトのライフ サイクル (新規/削除) を制御するコンテナーを作成しました。たとえば、コンテナに通知することなくオブジェクトが削除されます。オブジェクトは同じ基本クラス (GreetingBase) から継承します。

実装には、「テンプレート トリック」を使用しています。

class GreetingBase{
public:
  virtual void sayHello(){
    std::cout << "Hello there!" << endl;
  }

  virtual ~GreetingBase(){}
};

class GreetingDerived: public GreetingBase{
public:
  virtual void sayHello(){
    std::cout << "Hola amigos!" << endl;
  }
  virtual ~GreetingDerived(){}
};

class GreetingContainer{
public:
   template <class T>
   void addClass(){
      items.push_back(new T());
   }
~GreetingContainer(){
   for(std::vector<GreetingBase*>::iterator it = items.begin();
   it < items.end(); it++ ){
   delete *it;
 }
}

void talk(){
  for(std::vector<GreetingBase*>::iterator it = items.begin();
    it < items.end(); it++ ){
      (*it)->sayHello();
   }
  }
private:
 std::vector<GreetingBase*> items;
};


int main(){
  GreetingContainer container;
  container.addClass<GreetingDerived>();
  container.talk();
  return 0;
}

質問:

  1. この問題を解決するためにテンプレートを使用することは一般的なアプローチですか? 欠点はありますか?
  2. 「T」が「GreetingBase」から派生していない場合に、より適切なエラー メッセージを報告する「標準的な方法」

前もって感謝します。

4

3 に答える 3

2

この問題を解決するためにテンプレートを使用することは一般的なアプローチですか?

それが一般的かどうかはわかりませんが、十分に賢明に見えます。コンテナにはnew、で割り当てられたオブジェクトへのポインタのみを含めることができます。これは良いことです。

欠点はありますか?

主な問題は、コンテナーが 3 つのルールに違反していることです。暗黙的に生成されたコピー コンストラクターと、各ポインターを単純にコピーするコピー代入演算子があります。これにより、両方のデストラクタが同じオブジェクトを削除しようとする 2 つのコンテナ オブジェクトが得られます。

これを修正する最も簡単な方法は、これらのメンバー関数を削除するか、言語の 2011 年より前のバージョンで行き詰まっている場合は、それらを非公開 (実装なし) と宣言することです。コンテナをコピーできるようにする必要がある場合は、安全にコピーできるように実装する必要があります。

個人的には、独自の RAII コンテナーを展開するよりもスマート ポインターを使用します。std::unique_ptrコンテナーが排他的な所有権を持つ場合、所有std::shared_ptr権を共有する場合、またはstd::weak_ptr共有ポインターによって他の場所で管理されるオブジェクトへの非所有参照を保持する場合。過去に行き詰まっている場合はunique_ptr利用できませんが、Boost はshared_ptrweak_ptr、および同様のPointer コンテナーを提供します。

「T」が「GreetingBase」から派生していない場合に、より適切なエラー メッセージを報告する「標準的な方法」

C++11 では、おそらく次のような静的アサートを使用できます。

static_assert(std::is_base_of<GreetingBase, T>::value, 
              "Wrong type for GreetingContainer");

別の方法として、ローカル ポインターを作成することで、もう少し読みやすいエラー メッセージが表示される場合があります。その場合、エラー メッセージには少なくとも のフル ネームは含まれませんpush_back

GreetingBase * p = new T();
items.push_back(p);

エラー メッセージは次のようcan't convert Whatever* to GreetingBase*になります。

于 2012-09-25T10:28:10.807 に答える
1
  1. これに使用templateしても問題ありませんが、受け取る関数を使用するpointer to base-class方が優れています。
  2. 標準 C++11 の方法 - std::is_base_ofstatic_assertで使用します。C++11 を使用しない場合 - 同等のブースト、または独自のメタ関数。

補足: を使用する場合smart pointers、そのようなコンテナーを作成する必要はありません。

于 2012-09-25T10:20:37.577 に答える
0

addClassテンプレートを作成する利点はありません。これは、異なる型ごとにインスタンス化されたコードの新しいコピーを取得し、特定のコンストラクター呼び出しを強制していることを意味します。

基本クラスのポインターを取る単純な関数にするだけです。

void addClass(GreetingBase *o){
    items.push_back(o);
}

だからあなたの発信者はそうします

container.addClass( new GreetingDerived() );

それ以外の

container.addClass<GreetingDerived>();
于 2012-09-25T10:24:45.250 に答える