如何避免C++17中的虚拟继承?
让我们看看示例类。基类是ITransport,传输类接口:
class ITransport {
public:
virtual void move(const Path& p) = 0;
virtual double estimateTime(const Path& path) = 0;
/*Some more methods.*/
};
执行:
class Transport : public ITransport {
public:
virtual void move(const Path& p) override {
currPoint_ = p.lastPoint();
}
/*Some more methods.*/
private:
Point currPoint_;
};
我们还假设我们要创建一个自移动的运输类:
template <typename EnergySource>
class SelfMovingTransport : public Transport {
/*Some special methods for self moving transport.*/
};
自动运输最简单的例子是汽车:
template <typename EnergySource>
class Car : public SelfMovingTransport <EnergySource> {
public:
virtual void visitCarService() = 0;
/*Some more methods with logic for cars.*/
};
还需要造内燃机车……
class ICECar : public Car<Petrol> {
public:
virtual void move(const Path& p) override {
Transport::move(p);
/*Some special methods for ICECar.*/
}
virtual void visitCarService() override {
/*Visit closest ICECar service.*/
}
/*Some special methods for ICECar.*/
private:
Petrol::Amount petrol_;
};
...和电动汽车课程。
class ElectricCar : public Car<Electri?ity> {
public:
virtual void move(const Path& p) override {
Transport::move(p);
/*Some special methods for ElectricCar.*/
}
virtual void visitCarService() override {
/*Visit closest ElectricCar service.*/
}
/*Some special methods for ElectricCar.*/
private:
Electricity::Amount charge_;
};
这个逻辑的延续可以是,例如,添加火车类等:
template <typename EnergySource>
class Train : public SelfMovingTransport<EnergySource> {
/*Not interesting.*/
};
我使用c++17编译器(MS)。不多也不少。
我想创建一个std::vector<Car*>指向不同类型汽车的指针数组(或),并为它们调用一些常用方法。例如,有一种简单的方法将它们全部发送到服务(请参阅 参考资料Car::visitCarServeice())。
我试过树的想法:
- 创建类
ISelfMovingTransport和ICar:
class ISelfMovingTransport : public virtual ITransport { /*All the same.*/ }; class ICar : public virtual ISelfMovingTransport { /*All the same.*/ };改为
Transprot:class Transport : public virtual ITransport { /* All the same. */ }改为
SelfMovingTransport:template <typename EnergySource> class SelfMovingTransport : public ISelfMovingTransport, public Transport<EnergySource> {};改为
Car:template <typename EnergySource> class Car: public ICar, public SelfMovingTransport<EnergySource> { /*All the same*/ };最终解决方案没有奏效,因为
static_cast
不能用于将指针强制转换为虚拟派生类指针(请参阅 pastebin链接。)。无法编译示例代码(错误:无法从指向基类“ISelfMovingTransport”的指针转换为指向派生类“ElectricCar”的指针,因为基类是虚拟的)。当我想执行ElectricCar作为指向 a 的指针访问的操作时Car,我需要
dynamic_cast<ElectricCar*>(carPtr)wherecarPtris ofCar*。但是dynamic_cast是不允许的,RTTI是关掉的。
- 使用
std::vector<Transport*>对象并将其投射到Car. 它有效,但我不喜欢这个解决方案,因为很难检查一切是否正确。 - 使用
std::variant<ICECar, ElectricCar>和std::visit。(std::visit([](auto& car) -> void { car.visitCarServeice(); }, carV)). (现在用这种方法实现。)。
在这个例子中(它代表了一个真实项目中的一个问题)我不想改变逻辑(尤其是从一个Transport级别到另一个级别的类Car)。
在没有 RTTI 和 dynamic_cast 的情况下,有没有一种通用的方法来做所需的事情?
std::variant在这种情况下是否“OK”(假设汽车类别不因大小和/或内存不同而不同)?
问的问题,因为不知道如何谷歌。
PS 所有示例都是真实项目中情况的表示(模拟等)。我请你想象一下真的需要能量类型作为参数,而不是考虑复杂性(混合动力汽车等)。
PPS 在实际项目中,我需要一个“汽车”的唯一对象作为其他类的字段。
回答
我想创建一系列不同类型的汽车。
你不能。数组在 C++ 中是同构的。所有元素始终具有相同的类型。
使用
std::vector<Transport*>和投射对象到 Car
如果向量元素应该只指向汽车,那么使用指针来ICar代替似乎更有意义。此外,如果向量应该拥有指向的对象,那么您应该使用智能指针。并且您需要使析构函数成为虚拟的。
在没有 RTTI 和 dynamic_cast 的情况下,有没有一种通用的方法来做所需的事情?
通常是:虚函数。
在这种情况下 std::variant 是“OK”吗
如果变体的数量是一个小的常数,则可以。相比之下,继承层次结构允许添加任意多个子类。