代码之家  ›  专栏  ›  技术社区  ›  Shane

c++将类映射到数字

  •  2
  • Shane  · 技术社区  · 8 年前

    我最近开始从事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 回复  |  直到 8 年前
        1
  •  2
  •   Fureeish    8 年前

    我可以看到您的方法如何与 std::map<uint8_t, std::unique_ptr<Vehicle>> ,但有一个问题-您无法使用 initializer_list ,因为它 副本 我们都知道, std::unique_ptr 无法复制。您必须创建 init() 函数初始化映射,该映射将使用与 Vehicle *Vehicle::from_type(byte type) ,如果您已经有了自己的函数,那么这将毫无意义。

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

    最后一句话——你 能够 使用 std::map<uint8_t, std::shared_ptr<Vehicle>> 而不是 标准::映射(&L);uint8\u t,标准::唯一\u ptr<车辆>&燃气轮机; 并用 initializer\u列表 自从 std::shared_ptr 可以 被复制,但我不建议这样做,因为它错误地指示 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
    }
    

    另外需要注意的是,使用此解决方案的最后一个障碍是速度非常慢。它在地图中构造对象,除了将它们作为“模板化”副本示例保存外,对它们不做任何处理。

        2
  •  1
  •   metal    8 年前

    如果它们都派生自基类,则可以使用工厂模式,例如 Loki's implementation (参见 现代C++设计 关于细节,尽管那本书是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 .