在C ++中创建动态类型

时间:2013-04-16 21:36:37

标签: c++ templates design-patterns inheritance

我正在编写一个通用软件,它将被加载到相同基本硬件的许多不同变体上。它们都具有相同的处理器,但具有不同的外围设备和它们自己需要执行的功能。通过读取硬件开关值,软件将知道应该运行哪种变体。

这是我目前的实现,简而言之:

class MyBase
{
public:
    MyBase() { }
    virtual run() = 0;
}


class VariantA : public MyBase
{
public:
    VariantA () { }
    virtual run()
    {
        // Run code specific to hardware Variant-A
    }
}


class VariantB : public MyBase
{
public:
    VariantB () { }
    virtual run()
    {
        // Run code specific to hardware Variant-B
    }
}


void main()
{
    MyBase* variant;
    uint_8 switchValue = readSwitchValue();

    switch(switchValue)
    {
    case 0:
        variant = new VariantA();
        break;

    case 1:
        variant = new VariantB();
        break;
    }

    variant->run();
}

现在这很好用。我读取硬件值并使用switch语句创建新的相应类。

问题是我必须处理很多变种。目前约有15个,有可能在不久的将来再增加20-30个。我真的很鄙视那些运行数百行的switch语句,所以我真的想找到一种更好的方法,可能是通过模板。

我希望能够使用我的硬件值来查找类型并使用该类型来创建我的新对象。理想情况下,当我添加一个新变体时,我创建了新类,将该类类型添加到我的查找表中,并使用匹配的硬件值,这样做很好。

这有可能吗?这里有什么好的解决方案?

5 个答案:

答案 0 :(得分:16)

如前所述,你是一个工厂,但不一定是天真的开关语句。你可以做的是创建一个模板类来创建相关对象并动态地将它们添加到你的工厂。

class VariantinatorBase {
  public:
    VariantinatorBase() {}
    virtual ~VariantinatorBase() {}
    virtual std::unique_ptr<Variant> Create() = 0;
};

template< class T >
class Variantinator : public VariantinatorBase {
  public:
    Variantinator() {}
    virtual ~Variantinator() {}
    virtual std::unique_ptr<Variant> Create() { return std::make_unique<T>(); }
};

现在你有一个允许你注册这些的类工厂。

class VariantFactory
{
  public:
    VariantFactory()
    {
         // If you want, you can do all your Register() calls in here, and even
         // make the Register() function private.
    }

    template< uint_8 type, typename T >
    void Register()
    {
        Register( type, std::make_unique<Variantinator<T>>() );
    }

    std::unique_ptr<Variant> Create( uint_8 type )
    {
        TSwitchToVariant::iterator it = m_switchToVariant.find( type );
        if( it == m_switchToVariant.end() ) return nullptr;
        return it->second->Create();
    }

  private:
    void Register( uint_8 type, std::unique_ptr<VariantinatorBase>&& creator )
    {
        m_switchToVariant[type] = std::move(creator);
    }

    typedef std::map<uint_8, std::unique_ptr<VariantinatorBase> > TSwitchToVariant;
    TSwitchToVariant m_switchToVariant;
};

在程序开始时,创建工厂并注册您的类型:

VariantFactory factory;
factory.Register<0, VariantA>();
factory.Register<1, VariantB>();
factory.Register<2, VariantC>();

然后,你要打电话给它:

std::unique_ptr<Variant> thing = factory.Create( switchValue );

答案 1 :(得分:3)

您正在寻找工厂

http://www.oodesign.com/factory-pattern.html

工厂是一个软件模块(方法,类),其唯一目的是为作业创建正确的对象。使用工厂类的示例:

class VariantFactory
{
    MyBase* CreateObject(uint_8 value);
}

可以填写CreateObject方法,为您提供所需的对象类型。

如果选择的对象非常少,结构简单,那么简单的switch语句就足够了。一旦你得到很多需要更详细构造的物体或工厂,工厂就非常有用。

答案 2 :(得分:2)

我发表了评论;让我们把它变成一个答案:

就个人而言,我认为创建适当类的“switch / case”块可能是最佳解决方案。只需将case语句放在一个静态的“factory”方法中,该方法返回对特定类的引用。恕我直言......

以下是一个很好的例子:factory method design pattern

Class Book : public Product
{
};

class Computer : public Product
{
};

class ProductFactory
{
public:
  virtual Product* Make(int type)
  {
    switch (type)
    {
      case 0:
        return new Book();
      case 1:
        return new Computer();
        [...]
    }
  }
}

Call it like this:

ProductFactory factory = ....;
Product* p1 = factory.Make(0); // p1 is a Book*
Product* p2 = factory.Make(1); // p2 is a Computer*
// remember to delete p1 and p2

请注意,在他最出色的回应中,smink也提出了其他一些设计备选方案。

BOTTOM LINE:开关/外壳块没有任何内在的“错误”。即使对于具有许多案例选项的开关也是如此。

... IMHO

PS: 这真的不是创造“动态类型”。相反,它是“动态创建静态类型”。如果您使用模板或枚举解决方案,情况也同样如此。但同样 - 我非常喜欢“开关/案例”。

答案 3 :(得分:2)

更新:我将此原始解决方案留给后人,但考虑到paddy提供的解决方案优越且不易出错。只有一些轻微的改进,我认为它实际上和你可能得到的一样好。


考虑这个设计:

class VariantA : public MyBase
{
    static MyBase *CreateMachineInstance() { return new VariantA; }
};

class VariantB : public MyBase
{
    static MyBase *CreateMachineInstance() { return new VariantB; }
};

现在,您所需要的只是std::map,它使用uint_8作为键并将其映射到函数指针(返回MyBase)。在地图中插入标识符(指向适当的机器创建函数),然后读取代码并使用地图查找您正在使用的机器。

这是基于一个称为“工厂”的概念/模式,但如果您的机器构造函数需要不同的参数,或者您需要执行额外的每台机器初始化/操作,可能会略有破坏 - 从您提到的内容听起来就像您威力。

如果是这种情况,你仍然可以使用这种模式,但你必须做一些调整并重新设计一些东西,但你最终会得到一些很多更清洁,更容易扩充和维护。

答案 4 :(得分:-3)

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;

template<class T,class T1>
class HeroHonda
{
private:
    T millage;
    T1 *options;

public:
    HeroHonda() {
        puts("constructed");
        options=new T1[20];

        strcpy(options,"Good millage,Powerstart");
        millage=110;
    }

    virtual T features() {
        cout<<options<<"millage is"<<millage<<endl;
        return 1;
    }

    // virtual T Extrafeatures() = 0;

    ~HeroHonda() {
      cout<<"destructor"<<endl;
      delete [] options;    
    }
};

int main()
{
    HeroHonda <int,char> *Ptr=new HeroHonda <int,char>;
    Ptr->features();
    delete Ptr;
}