4

当我必须扩展一个类的行为而不修改它时,我经常使用设计模式visitor。它添加了类似成员的函数,而不修改它所使用的类的核心。

或多或少以同样的方式,我需要扩展一个第三方类,但主要是数据,而不是行为。

在这种情况下,我经常使用 std::map 匹配 a keyMyClass*和 value MyClassExtender。MyClassExtender 包含所有附加信息。

在这样做的同时,我碰巧想知道是否还有其他方法可以做到这一点,可能更常见或更“最佳实践”。我应该将这个附加类称为 Extender 吗?这种模式是否有名称......

Nota Bene:我可以简单地将 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 回答