【问题标题】:Can I use class type in map<char[2],class> as container?我可以在 map<char[2],class> 中使用类类型作为容器吗?
【发布时间】:2016-05-19 15:55:54
【问题描述】:

我想要做什么:我从 XML 文件加载数据(这很好)。 XML 告诉程序创建类实例(来自特定的不同类,如CarPlane),然后这些实例将存在于程序中。这些类实例对象是总体Object 基类的子类。

XML文件以数字的形式存储了需要创建什么类型的对象,程序会根据这个数字确定创建哪个类的对象。

我可以使用switch 语句并枚举所有类型的对象,但是当添加大量对象实例时,这对性能的影响非常大。因此,我想要一个从 char[2] 到所需类的映射。 (注意:对于类类型本身,而不是类的实例。)

例如,如果 XML 属性为 'type=0x01',则将生成来自类 Car 的对象,而如果是 'type=0x02',则将生成 Plane 对象。 (两者都是Object

这样,我想使用一个常量映射来完成这项工作。我想写类似的东西

map<char[2],class> ObjectsList = {
     {0x01,Car},
     {0x02,Plane}
     //etc, etc.
}

...
// while loading data from xml file on which objects need to get created,
// an object-tag gives data from 'type' attribute, and the program stores
// it in 'char[2] type';
char[2] type = getattributestufffromxml("type");
ObjectsList[type] newObject = memoryallocator.allocate(sizeof(ObjectsList[type]);
newObject.Init(); //inherited function from Object

这样做的想法是创建一种更快的方法,而不是坚持使用 switch 语句,这在创建数百个对象时很糟糕。

我需要做什么才能从上面得到有效的 C++?我不知道如何在地图中存储类类型。 (我收到编译器错误,例如“参数 2/4 无效”)

【问题讨论】:

  • 有人知道我该如何解决这个问题吗?它有什么问题?
  • 为了清楚起见,我添加了一些额外的解释。问题是;我想将类类型作为参考存储在地图中,因此很容易引用。
  • 你是故意slicing your objects在那张地图上吗?
  • @Generaal 看起来更像是我使用std::map&lt;uint8_t,Vehicle*&gt; 的案例。
  • @WhozCraig:不,这不是我的意图。

标签: c++ xml dictionary


【解决方案1】:

我不知道如何在地图中存储类类型。 (我经常得到“参数 2/4 无效”)

好吧,char[2] 数组不能用作std::map::key_type,因为它不满足Compare concept 的必要约束。
从您的初始化列表看来,您无论如何都想要一个 uint8_t 作为键值。

您也不能将类型作为值存储在您的地图中。

您是否应该为构造函数使用函数指针,that's not possible,因为构造函数和析构函数是特殊的野兽(没有返回类型,甚至 void),您不能用函数指针引用它们。


我认为您真正需要的是一个 std::map 存储与 enum 对应的工厂函数,例如:

enum class VehicleTypes : uint8_t {
    CarType = 0x01 ,
    PlaneType = 0x02 ,
}; 

struct Vehicle {
     virtual ~Vehicle() {}
     virtual void Init() = 0;
};

typedef std::function<Vehicle*()> CreateVehicleFn;

class Car : public Vehicle {
public:
     virtual void Init() {
         // do anything necessary to init a Car
     }
};

class Plane : public Vehicle {
public:
     virtual void Init() {
         // do anything necessary to init a Plane
};

std::map<VehicleTypes,CreateVehicleFn> CreatorFnList = 
     { { VehicleTypes::CarType, []() { return new Car(); } } ,
       { VehicleTypes::PlaneType, []() { return new Plane(); } }
     };

后面的地图初始化列表并不比您在(伪)代码示例中提供的代码多多少。
如果你想摆脱样板的东西,并认为值得混淆你的代码,你仍然可以使用宏:

#define Create(classtype) []() { return new classtype(); }

std::map<VehicleTypes,CreateVehicleFn> CreatorFnList = 
     { { VehicleTypes::CarType, Create(Car) } ,
       { VehicleTypes::PlaneType, Create(Plane) }
     };

您可以稍后根据关键参数使用它来创建新实例:

Plane* plane = dynamic_cast<Plane*>(CreateVehicleFn[VehicleTypes::PlaneType]());
if(plane) {
    plane->Init();
}

为了提供清晰的所有权语义,甚至可以考虑使用std::unique_ptr&lt;Vehicle&gt; 将新实例从工厂传递到客户端:

typedef std::function<std::unique_ptr<Vehicle>()> CreateVehicleFn;

std::map<VehicleTypes,CreateVehicleFn> CreatorFnList = 
     { { VehicleTypes::CarType, []() { return make_unique<Car>(); } } ,
       { VehicleTypes::PlaneType, []() { return make_unique<Plane>(); } }
     };

用法的语法与我的第一个示例大致相同:

std::unique_ptr<Plane> plane = CreateVehicleFn[VehicleTypes::PlaneType]();
if(plane.get()) {
    plane->Init();
}

【讨论】:

    【解决方案2】:

    在 C++ 中,类是(编译类型)定义,而不是(运行时)数据对象。所以你不能存储一个类。

    但是,您可以声明工厂类型,并存储工厂的实例。

    例如:

    class Factory
    {
         virtual std::unique_ptr<Object> createInstance(std::string description);
    }
    
    class CarFactory : public Factory
    {
         std::unique_ptr<Object> createInstance(std::string description) override;
    }
    class PlaneFactory : public Factory
    {
         std::unique_ptr<Object> createInstance(std::string description) override;
    }
    

    然后我们存储工厂:

    std::map<const char[2],Factory*> factories = {
         {"00", new CarFactory},
         {"01", new PlaneFactory},
         //...
    }
    

    你也许可以创建一个通用工厂:

    template<typename T>
    class VehicleFactory : public Factory
    {
         std::unique_ptr<Object> createInstance(std::string description) override
         { return std::make_unique<T>(description); }
    }
    
    std::map<int,Factory*> factories = {
         {"00", new VehicleFactory<Car>},
         {"01", new VehicleFactory<Plane>},
         //...
    }
    

    一旦我们有了工厂,我们就可以使用它:

    std::unique_ptr<Object> createVehicle(const char type[2], std::string description)
    {
        // error handling is an exercise for the reader
        return factories[type]->createInstance(description);
    }
    

    【讨论】:

    • 为什么不等待并发布完整的答案?
    • @Toby Bit old school style
    • @Nathan - 主要原因是我从审查低质量问题来到这里,并且已经获得了三票接近的投票。我想表明,尽管问题不清楚,但仍然存在一个好的答案,并且如果问题被关闭,也不会失去太多的工作。第二个目的是节省其他回答者考虑写一些非常相似的东西的时间(尽管我通常不会仅仅因为这个原因而发布半个答案)。
    • @πάντα - 是的,我知道;我希望这对提问者来说会更清楚。我赞成你的答案,我认为这两个答案一起给出了一个很好的画面。
    • @TobySpeight “我认为这两个答案一起给出了一个很好的画面”当然。
    猜你喜欢
    • 2012-07-25
    • 1970-01-01
    • 2020-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多