3

私はライブラリを書いていますが、その中にクラス BaseClass があります。ライブラリを使用する人は誰でも、BaseClass から継承する独自のクラスを作成します。別のクラスがあります。それを Manager と呼びましょう。これは BaseClass ポインターのベクトルを保持し、BaseClass から派生した任意のオブジェクトを保持できます。

Manager クラスは、その BaseClass ベクターに追加されたオブジェクトの作成と破棄を処理する必要があります。これは、ベクトル内の任意のオブジェクトをいつでも削除でき、マネージャー自体も削除できるためです。このため、ライブラリのユーザーは、BaseClass から派生した既存のオブジェクトへのポインタを渡すことによって Manager の baseClass ベクトルにオブジェクトを追加することはできません。実際には、ユーザーにそれを許可することができました。しかし、それにはダミー オブジェクトをコピーする必要があり、私はそうしたくありません。

これを解決するために、テンプレート関数を使用しようとしています。ユーザーは、BaseClass から派生したオブジェクトを Manager のベクターに追加しようとするときに、オブジェクトの型を渡す必要があります。これは私が現在持っているものです。

//Manager.h
#include <vector>
#include "BaseClass.h"
#include <typeinfo>

class Manager {
    //Vector holding pointers to any objects inherited from BaseClass
    vector<BaseClass*> baseClasses;

    //Template function that needs to add NEW object to baseClass vector
    //This I'm having problems with
    template<class T>
    BaseClass* Add() {
        BaseClass* baseClass = new T();
        baseClasses.push_back(baseClass);
        return baseClass;
    }

    //Template function that gets object from baseClass vector
    //This works fine
    template<class T>
    BaseClass* Get() {
        for (int i = 0; i < baseClasses.size(); i++) {
            if (typeid(*baseClasses[i]) == typeid(T)) {
                return baseClasses[i];
            }
        }
        return NULL;
    }
};

たとえば、ユーザーは Manager の baseClass ベクターにオブジェクトを追加または取得するときにこれを行う必要があります。DerivedClass は BaseClass から派生します

Manager manager;
//Add a new DerivedClass object to Manager's vector
manager.Add<DerivedClass>();
//Get a pointer to the DerivedClass object that was just added
DerivedClass* derivedClass = (DerivedClass*)manager.Get<DerivedClass>();

私の Get() 関数は正常に動作します。私が知る必要があるのは、Add() 関数を機能させるにはどうすればよいかということです。助けていただければ幸いです。

4

1 に答える 1

7

設計について不明な点がかなりありますがT、メンバ関数 templaの型Addが から派生するように強制できるかどうかが問題である場合BaseClass、オプションは単純です。

  • 何もしないと、コンパイラは喜んで次の行に文句を言いますBaseClass* baseClass = new T();
  • これをより明確にするために静的アサートを追加します
  • ファンシーな SFINAE トリックを使用して、一連のオーバーロードから関数を削除します

私は最初の2つのどちらかを選びます。静的アサートは次のように綴ることができます。

static_assert(std::is_base_of<BaseClass,T>::value);

SFINAE のトリックは、コードをより混乱させるので、私は本当に避けたいと思いますが、次のように実装できます。

template <class T>
typename std::enable_if<std::is_base_of<BaseClass,T>::value,T*>::type
Add() {
   baseClasses.push_back(new T());
   return baseClasses.back();
}

(戻り値の型を に変更したことに注意してくださいT*。オブジェクトは aです。Tなぜ a を返すのですか?オブジェクトが実際に a であることがわかっている場合、a を返す意味がない関数にもBaseClass*同じことが当てはまります)GetBaseClass*T

実際の問題ははるかに複雑です。なぜなら、あなたのデザインは積極的に所有権の考慮を避けているからです。オブジェクトの所有権について考え、明確な所有者がいること (またはリソースが共有されていること) を確認してください。オブジェクトの所有者がわかったら、オブジェクトを削除する必要があるときに所有者に通知するための残りのコードのプロトコルを作成します。保持しているポインターをコードで削除できるようにすると、すぐに未定義の動作が発生します。

その他のささいな懸念としては、すべてのコンポーネントにデフォルトのコンストラクターがあることを強制しているという事実が含まれる可能性がありますが、これは適切である場合とそうでない場合があります。Addポインターを受け取るテンプレート化されていないものを用意し、呼び出し元が好きなようにオブジェクトを作成できるようにすることで、この制限を簡素化できます。

の使用typeidは通常コード臭であり、これは例外ではないと思います。実行するのではなく、オブジェクトが何であるかを尋ねることができる型階層を設計する方がよいでしょうtypeid。型に基づいてインターフェイスを作成することに本当に決心している場合は、dynamic_castより良い方法があるかどうかを検討してください。より非効率的になりますが、複数レベルの継承がある場合、最も派生したオブジェクトを中間オブジェクトとして返すことができます

于 2013-11-06T22:42:43.790 に答える