我最近开始使用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]();
}
答案 0 :(得分:2)
我可以看到您的方法如何与std::map<uint8_t, std::unique_ptr<Vehicle>>
一起使用,但是存在问题 - 您无法使用initializer_list
初始化该地图,因为它复制< / em>元素,众所周知,std::unique_ptr
无法复制。您必须创建一个init()
函数来初始化将使用与Vehicle *Vehicle::from_type(byte type)
类似的逻辑的地图,如果您已经拥有自己的功能,这将毫无意义。
此外,我不同意你的第一个解决方案违反DRY。从某种意义上说,它实际上是正确的,您不会被迫在代码中的其他地方使用switch
或if
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 上运行。