質問

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

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

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

//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;
    }
};

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

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()関数を動作させることができるかということです。ヘルプは大歓迎です。

役に立ちましたか?

解決

あなたのデザインには不明なことはかなりありますが、質問があなたがそのタイプを強制できるかどうかである場合 T メンバー関数テンプラ 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, 、なぜ返すのか BaseClass*?同じことが Get 関数aを返すことには意味がありません BaseClass*, 、あなたがオブジェクトが本当に T)

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

他のより少ない懸念には、すべてのコンポーネントがデフォルトのコンストラクターを持っていることを実施しているという事実が含まれます。非テンプレートを使用することにより、この制限を簡素化できます Add それはポインターを取り、発信者に彼らが好きなようにオブジェクトを作成させることができます。

の用法 typeid 通常、コードスメルであり、これが例外ではないと思います。たぶん、オブジェクトが何であるかを実行するのではなく尋ねることができるタイプの階層を設計する方が良いでしょう。 typeid. 。タイプに基づいてインターフェイスを作成することを本当に決心している場合は、 dynamic_cast もっとよくなるはず。より無関係になりますが、複数のレベルの継承がある場合、中間オブジェクトとして最も派生オブジェクトを返すことができます

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top