C ++:创建映射到枚举的某种类型的对象

时间:2011-02-11 11:53:49

标签: c++

考虑以下代码示例:

class Base;
class A; class B; class C; //A, B and C are inherited from Base

enum TypeID
{
   TYPE_A = 0, TYPE_B, TYPE_C, TYPE_MAX;
}

class SomeContainer
{
    private Base* objects[ TYPE_MAX ];


    public void Add( TypeID what )
    {
        if( objects[ what ] ) return;

        switch( what )
        {
            case TYPE_A: objects[ TYPE_A ] = (Base*) new A;
            case TYPE_B: objects[ TYPE_B ] = (Base*) new B;
            case TYPE_C: objects[ TYPE_C ] = (Base*) new C;
            default:     printf( "Invalid type\n" ); return NULL;
        }
    }

    public A* GetA()
    {
        return (A*) objects[ TYPE_A ];
    }

    public B* GetB()
    {
        return (B*) objects[ TYPE_B ];
    }

    public C* GetC()
    {
        return (C*) objects[ TYPE_C ];
    }

}

我认为描述我的真实系统目前所做的事情比言辞更好。

现在实际上我有更多来自base的类,它大约是15。

  • 我是否有从Base派生的所有类的重复代码?在switch语句中为它们添加一行,还有一个辅助函数从数组中获取它们吗?

  • 有没有办法实现自动化?

6 个答案:

答案 0 :(得分:4)

这些方面应该有效:

class A; class B; class C;

template<class T>
struct type_index;

template<>
struct type_index<A> { static const size_t index = 0; };
template<>
struct type_index<B> { static const size_t index = 1; };
template<>
struct type_index<C> { static const size_t index = 2; };


template<class T>
public Base* Add() {
    size_t index = type_index<T>::index;
    if( !objects[index] )
       objects[index] = new T;
    return objects[index];
}

template<class T>
public T* Get() {
    return (T*) objects[type_index<T>::index];
}

然后,您可以使用cont.Add<A>()创建A对象,使用cont.Get<B>()来接收B对象。

如果实际上这是一个好主意,取决于你为什么要这样做......

答案 1 :(得分:2)

使用模板减少一些开销:

enum TypeID { ... } // as before

#define DECLARE_TYPE(_TYPEID) public: enum { myTypeID = _TYPEID }

// for each class
class A
{
    DECLARE_TYPE(TYPE_A); // creates a public enum we can access
};

// B, C, D, etc

// Add() as normal, but only one Get() function:
template<typename T>
T* get() 
{
    return static_cast<T*>(objects[T::myTypeID]);
}

用法:

B* const bInstance = get<B>();

答案 2 :(得分:1)

您应该使用模板来完成工作。


这是我如何做到这一点的一个例子:

class Base {};
class A : public Base { public: static const int type = TYPE_A; };
class B : public Base { public: static const int type = TYPE_B; };
class C : public Base { public: static const int type = TYPE_C; };

template< typename T> void SomeContainer::Add( )
{
    if( T::type >= TYPE_MAX ) {
        printf( "Invalid type\n" );
    }
    if( objects[ T::type ] ) return;

    objects[ T::type ] = new T;
}

答案 3 :(得分:1)

与经典抽象工厂模式类似。粗略的轮廓在这里:

class BaseFactory 
{
public:
  virtual ~BaseFactory();
  virtual Base * createBase() const = 0;
}

template< typename T >
class BaseFactoryImpl : public BaseFactory
{
public:
  Base * createBase() const
  {
     return new T;
  }
};

std::map< key_type, BaseFactory * > baseFactoryTable;
// populate the map with instances of factories for the classes somewhere

// create an object

std::map< key_type, BaseFactory * >::const_iterator iter = baseFactoryTable.find(key);
if( iter!= baseFactoryTable.end() )
{
   return iter->second->createBase();
}
// else throw or return NULL

在我看到你根据传入的enumaration“获取”一个类的情况。将它们全部添加到表中的一种方法是在这里向BaseFactoryImpl添加一个额外的参数,以将它与该类的枚举相关联。 ,然后你可以自动将它自己添加到地图上。

将工厂用于您的类型而不是您的类型的目的是处理您的惰性评估模型,从而仅在第一次使用时创建实际实例。在多线程环境中,您应该使用boost :: once来执行此操作。

答案 4 :(得分:0)

Kris,使用具有多态性和模板的抽象工厂。这是something to start with。您可以通过从模板类型列表向工厂添加类型来进一步开展工作。

答案 5 :(得分:0)

如果你足够勇敢(或者愚蠢 - 无论你怎样看待它!;))你可以使用boost mpl,这是一个简单的例子..

#include <iostream>     
#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>

namespace mpl = boost::mpl;

enum TypeID
{
   TYPE_A = 0, TYPE_B, TYPE_C, TYPE_D
};

template <int N>
struct foo
{
  foo() { std::cout << "foo<" << N << ">" << std::endl; }
};

template <int Index>
struct type_at
{
  // this is the type container..
  typedef typename mpl::vector<foo<0>, foo<1>, foo<2>, foo<3> > seq;
  // this allows us to get the type at the given index
  typedef typename mpl::at<seq, mpl::int_<Index> >::type type;
};

int main(void)
{
  // define type based on the index.
  type_at<TYPE_A>::type insta;
  type_at<TYPE_B>::type instb;
  type_at<TYPE_C>::type instc;
  type_at<TYPE_D>::type instd;
  return 0;
}