克隆设计模式适配器 - 避免切片子项(类似于原型模式)

时间:2018-03-29 00:53:44

标签: c++ design-patterns adapter clone prototype-pattern

我有一个案例,我有一个指向基类的指针容器,其中一些指针实际上指向派生类的对象。我需要在容器中创建每个对象的副本,而不会在复制过程中将对象的派生“部分”切掉。

执行此操作的常用方法是实现类似“虚拟父克隆()”方法的操作。这要求层次结构中的每个类都使用自己的类型实现自己的clone()方法。这要求未来未写的子类实现父类所期望的行为,但无法强制执行。相反,我写了一个派生类可以继承的“适配器”类,而不是直接从基类继承。通过这样做,我可以在尚未写入的子类中强制执行一致性。

适配器类(层次结构中的中间级,父级和子级之间)确实需要了解子类,以便为正确的子类类型调用“new()”。这种“感觉”就像它有点违反了传统的面向对象模型,因为它让父母知道从中继承的类。但是,从通用编程的角度来看,这可能是合理的。它确实似乎编译和工作。

我正在寻找任何批评,需要注意的事项等。这些代码是否100%便携且符合标准?我可以使用更好的设计模式吗?这个适配器是否对其他人有用?谢谢,

肖恩

#include <cstdlib>
#include <iostream>
#include <stdio.h>

//-----------------------------------------

//Don't need to forward-declare bar; just needs to be defined before first instantiation

template<typename A, typename B>
class foo
{
public:
    foo()
    {
        printf("foo::foo() : constructed a foo.\n");
    }

    virtual foo<A, B>* clone()
    {
        printf("foo::clone() : making a clone...\n");
        return new foo<A, B>;
    }

    virtual void DoSomething()
    {
        printf("foo::DoSomething() : something...\n");
    }

    A myA;
    B myB;
};

template<typename A, typename B, typename ChildClass>
class fooAdapter : public foo<A, B>
{
public:
    fooAdapter()
    {
        printf("fooAdapter::fooAdapter() : constructed a fooAdapter.\n");
    }

    foo<A, B>* clone()
    //or directly take in foo<>'s complete type rather than parametric per-parameter
    {
        return new ChildClass( *((ChildClass*) this) );
    }
};

//-----------------------------------------

class bar : public fooAdapter<int, float, bar>
{
public:
    bar()
    {
        printf("bar::bar() : constructed a bar.\n");
    }

    bar(const bar& myBar)
    {
        printf("bar::bar(bar) : copy-constructing a bar.\n");   
    }

    virtual void DoSomething()
    {
        printf("bar::DoSomething() : something.\n");
    };
    //don't need to include clone() in bar
};

//-----------------------------------------

int main(int argc, char *argv[])
{
    printf("About to construct myBar.\n");
    bar myBar;
    myBar.myA = 2;
    myBar.myB = 1.5;

    printf("\nAbout to clone myBar to fooTest.\n");
    foo<int, float>* fooTest = myBar.clone();
    fooTest->DoSomething();

    printf("\nAbout to construct fooTest2 from fooTest.\n");
    foo<int, float>* fooTest2 = new bar( *((bar*) fooTest) );

    return EXIT_SUCCESS;
}

1 个答案:

答案 0 :(得分:0)

停止使用哑指针。

使用cast-to-base操作或值ptr以及直接构造值ptr&#39的值的make值函数写入增强的any。

两者都知道对象的动态类型,并且因为他们拒绝自己指针,所以不太可能以多态方式创建。

template<class Base>
struct poly_any:private std::any{
  Base* operator->(){ return to_base(*this); }
  template<class X, std::enable_if<std::is_base_of<Base, std::decay_t<X>>{}, bool> =true>
  poly_any(X&& x):
    std::any(std::forward<X>(x)),
    to_base([](std::any& self)->Base*{
      return std::any_cast<std::decay_t<X>*>(&self);
    })
  {}
  poly_any(poly_any const&)=default;
  poly_any(poly_any &&)=default;
  poly_any&operator=(poly_any const&)=default;
  poly_any&operator=(poly_any &&)=default;
private:
  using to_base_t = Base*(*)(std::any&);
  to_base_t to_base=nullptr;
};

poly_any<Base>表示从Base派生的任何类型的多态vallue类型。它需要一些工作,但这是它的核心。

我们不会要求任何人写clone,我们会使用any来键入擦除复制和自定义类型擦除转换为基础。

您仍然可以对此进行切片,但这是在您第一次将某些内容放入poly_any时发生的。

如果您缺少

std::any,则可以写出,或者您可以使用boost::any。可能存在拼写错误。