4

クラスの動作を変更せずに拡張する必要がある場合は、デザインパターンビジターを使用することがよくあります。動作するクラスのコアを変更せずに、メンバーのような関数を追加します。

多かれ少なかれ同じように、私はサードパーティのクラスを拡張する必要がありますが、ほとんどの場合、動作ではなくデータを使用します。

このような場合、キーMyClass*と値を一致させるstd::mapをよく使用しますMyClassExtender。MyClassExtenderには、すべての追加情報が含まれています。

それをしている間、私はそれを行う他の方法があるのだろうか、おそらくもっと一般的またはより「ベストプラクティス」だろうかと思いました。この加法クラスをエクステンダーと呼ぶべきですか?そのようなパターンの名前はありますか...

注意:MyClass *とMyClassExtenderを新しいクラスに単純に集約することもできますが、MyClass *を指定してMyClassExtenderに頻繁にアクセスする必要があるため、st::mapは非常に便利です。

4

2 に答える 2

2

クラスをサブクラス化してみませんか?継承は、動作または状態に関係なく、クラスを拡張する方法です。クラスのインスタンスを他のデータに関連付けるだけの場合を除いて、その場合はまったく拡張されておらず、std::mapが正解です。

于 2012-10-22T10:42:53.793 に答える
1

つまり、拡張オブジェクトを使用して構造体にMyClassオブジェクトを作成します。

struct MyClassEx {
  MyClassExtension extension;
  MyClass object;
};

さまざまなタイプの堅牢性を高めるには、次の例のテンプレートを使用します:http: //ideone.com/mmfK83

以下の解決策は以下に触発されていstd::shared_ptr/std::make_sharedます:

template <typename Type>
struct LinkExtension;

template <typename Type>
struct TypeEx {
    using Extension = typename LinkExtension<Type>::Type;

    alignas(Type) uint8_t objectData[sizeof(Type)];
    alignas(Extension) uint8_t extensionData[sizeof(Extension)];

    Type* getObject() { return reinterpret_cast<Type*>(objectData); }
    const Type* getObject() const { return reinterpret_cast<const Type*>(objectData); }
    Extension* getExtension() { return reinterpret_cast<Extension*>(extensionData); }
    const Extension* getExtension() const { return reinterpret_cast<const Extension*>(extensionData); }

    template <class... Args>
    TypeEx(Args&&... args) 
    {
        new (objectData) Type(std::forward<Args>(args)...);
        new (extensionData) Extension();
    }
    ~TypeEx() 
    {
        getObject()->~Type();
        getExtension()->~Extension();
    }
    TypeEx(const TypeEx&) = delete;
    TypeEx& operator = (const TypeEx&) = delete;
};

そして、いくつかのヘルパー関数:

template <typename Type, class... Args>
Type* createObjectEx(Args&&... args)
{
   TypeEx<Type>* retVal = new TypeEx<Type>(std::forward<Args>(args)...);
   return retVal->getObject();
}

template <typename Type>
typename LinkExtension<Type>::Type& getObjectEx(Type* obj)
{
   static_assert(std::is_standard_layout<TypeEx<Type>>::value, "Oops");
   static_assert(offsetof(TypeEx<Type>, objectData) == 0, "Oops");
   TypeEx<Type>* retVal = static_cast<TypeEx<Type>*>((void*)obj);
    return *(retVal->getExtension());
}

template <typename Type>
const typename LinkExtension<Type>::Type& getObjectEx(const Type* obj)
{
    static_assert(std::is_standard_layout<TypeEx<Type>>::value, "Oops");
    static_assert(offsetof(TypeEx<Type>, objectData) == 0, "Oops");
    const TypeEx<Type>* retVal = static_cast<const TypeEx<Type>*>((const void*)obj);
    return *(retVal->getExtension());
}

template <typename Type>
void deleteObjectEx(const Type* obj)
{
    const TypeEx<Type>* objectEx = static_cast<const TypeEx<Type>*>((const void*)obj);
    delete objectEx;
}

そして、拡張機能をクラスにリンクする方法:

class MyClass {
public:
   virtual ~MyClass() = default; 
};
struct MyClassExtension {
    int a;
    int b;
};
template <>
struct LinkExtension<MyClass> {
    using Type = MyClassExtension;  
};

そしてそれが機能することを証明します:

void printExtension(MyClass* object);
int main() {
    MyClass* object = createObjectEx<MyClass>();
    MyClassExtension& extension = getObjectEx(object);
    extension.a = 1;
    extension.b = 2;
    printExtension(object);
    deleteObjectEx(object);

    TypeEx<MyClass> objectEx;
    objectEx.getExtension()->a = 3;
    objectEx.getExtension()->b = 4;
    printExtension(objectEx.getObject());
}

void printExtension(MyClass* object)
{
    MyClassExtension& extension = getObjectEx(object);
    std::cout << extension.a << ' ' << extension.b << std::endl;
}

コンパイラが可変個引数テンプレートをサポートしていない場合でも、解決策は可能ですが、完了するにはさらに多くの手作業が必要です。

于 2012-10-22T10:42:11.623 に答える