c ++将类映射到数字

时间:2017-12-28 13:22:15

标签: c++ mapping

我最近开始使用c ++开发。我已经遇到了一个我无法解决的问题,因为我不知道以下是否可能。

  

我想在数字和类之间创建一个映射,它是从抽象类派生的。

基本上我希望能够做的是创建一个工厂方法,该方法可以根据与该类关联的给定数字创建类的新实例。

我知道我可以做以下事情......

Vehicle *Vehicle::from_type(byte type)
{
    switch(type)
    {
        case 0x00: return new Bicyle();
        case 0x01: return new Car();
        ...
        case 0x10: return new Truck();
    }

    return null;
}

...,但我不想因为我想保持干燥。

有一种方法可以做到这一点:

// I know this is incorrect syntax
const map<byte, class extends Vehicle> VEHICLE_MAPPING = {{0x00, Bicyle}, {0x01, Car}, ..., {0x10, Truck}};

Vehicle *Vehicle::from_type(byte type)
{   
    return new VEHICLE_MAPPING[type]();
}

2 个答案:

答案 0 :(得分:2)

我可以看到您的方法如何与std::map<uint8_t, std::unique_ptr<Vehicle>>一起使用,但是存在问题 - 您无法使用initializer_list初始化该地图,因为它复制< / em>元素,众所周知,std::unique_ptr无法复制。您必须创建一个init()函数来初始化将使用与Vehicle *Vehicle::from_type(byte type)类似的逻辑的地图,如果您已经拥有自己的功能,这将毫无意义。

此外,我不同意你的第一个解决方案违反DRY。从某种意义上说,它实际上是正确的,您不会被迫在代码中的其他地方使用switchif s。我肯定会坚持下去。

最后一点 - 您可以使用std::map<uint8_t, std::shared_ptr<Vehicle>>代替std::map<uint8_t, std::unique_ptr<Vehicle>>并使用initializer_list进行初始化,因为std::shared_ptr 可以< / em>被复制,但我不会建议,因为它错误地指示shared_ptr的使用。如果你以某种方式感到被迫这样做,这是一个例子:

class Base{ public: virtual ~Base() = default; };
class Derived1 : public Base{};
class Derived2 : public Base{};

class derived_factory{
    private:
        derived_factory();
        static inline std::map<uint8_t, std::shared_ptr<Base>> base_map = {
            {0x00, std::make_shared<Derived1>()},
            {0x01, std::make_shared<Derived2>()}
        };
    public:
        static std::unique_ptr<Base> from_type(uint8_t type)
        {
            return std::make_unique<Base>(*base_map[type]);
        }
};

int main()
{
    auto ptr = derived_factory::from_type(0x00);
    // ptr is of a type std::unique_ptr<Base> and points to Derived1 object
}

附加说明应该是使用此解决方案的最后一个障碍是,它很慢。它构造了地图中的对象,除了将它们保留为“模板化”的复制示例外,它们对它们没有任何作用。

答案 1 :(得分:1)

如果它们都来自基类,您可以使用工厂模式,例如来自Loki's implementation(参见 Modern C ++ Design 获取详细信息,尽管该书是预先的-C ++ 11)。

以下创建了一些具体的工具并将它们放在一个向量中,然后在每个工具上调用drive()方法:

#include <iostream>
#include <memory>
#include <vector>
#include "factory.h"

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

struct Car : Vehicle
{
  static constexpr auto ID = 1;
  void drive() override { std::cout << "Car\n"; }
};

struct Truck : Vehicle
{
  static constexpr auto ID = 2;
  void drive() override { std::cout << "Truck\n"; }
};

// Create the factory object
auto g_factory = MyUtil::Factory<std::unique_ptr<Vehicle>, int>{};

void RegisterTypesWithFactory()
{
    // We pass in creator functions for each type. Note that these
    // could be lambdas or some other freestanding function and they
    // could accept parameters.
    g_factory.Register( Car::ID,   &std::make_unique<Car> );
    g_factory.Register( Truck::ID, &std::make_unique<Truck> );
}

int main()
{
    // Configure the factory
    // Note: Registration can be done any time, e.g., later based on input 
    // from a file. I do them all at once here for convenience of illustration.
    RegisterTypesWithFactory();

    // Create some objects with the factory
    auto vehicles = std::vector<std::unique_ptr<Vehicle>>{};
    vehicles.emplace_back( g_factory.Create( Car::ID ) );
    vehicles.emplace_back( g_factory.Create( Truck::ID ) );

    // Do something with the objects
    for( const auto& v : vehicles )
    {
        v->drive();
    }
}

打印哪些:

Car
Truck

看到它在 Wandbox 上运行。