问题描述
让我们看看示例类。基类为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 link。)。
无法编译示例代码(错误:无法从指向基类“ISelfMovingTransport”的指针转换为指向派生类“ElectricCar”的指针,因为基类是虚拟的)。
当我想用 ElectricCar
进行操作时
作为指向 Car
的指针访问,我需要
dynamic_cast<ElectricCar*>(carPtr)
其中 carPtr
是 Car*
。
但不允许使用 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”(假设汽车类没有
大小不同和/或内存不重要)?
问的问题,因为不知道如何谷歌。
附言所有示例都是实际项目中情况的表示(模拟等)。我请你想象一下真的需要能量类型作为参数,而不是考虑复杂性(混合动力汽车等)。
P.P.S.在实际项目中,我需要一个“汽车”的唯一对象作为其他类的字段。
解决方法
我想创建一系列不同类型的汽车。
你不能。数组在 C++ 中是同构的。所有元素始终具有相同的类型。
使用 std::vector<Transport*>
并将对象投射到 Car
如果向量元素应该只指向汽车,那么使用指向 ICar
的指针似乎更有意义。此外,如果向量应该拥有指向的对象,那么您应该使用智能指针。您需要将析构函数设为虚拟。
在没有 RTTI 和 dynamic_cast 的情况下,有没有一种通用的方法可以做所需的事情?
通常是:虚函数。
在这种情况下 std::variant 是否“OK”
如果变体的数量是一个小的常数,则可以。相比之下,继承层次结构允许添加任意多个子类。
,我相信这解决了您的问题,但很难说。它删除了代码中的一些细节,但演示了该技术。它使用 std::variant
和 std::visit
。
#include <iostream>
#include <memory>
#include <variant>
class Car {
public:
Car( ) = default;
};
class ICECar : public Car {
public:
ICECar( ) {
}
void visitCarService( ) {
std::cout << "ICE visitCarService\n";
}
};
class ECar : public Car {
public:
ECar( ) {
}
void visitCarService( ) {
std::cout << "E visitCarService\n";
}
};
using car_variant = std::variant<
std::shared_ptr<ICECar>,std::shared_ptr<ECar>>;
template <size_t C>
void visitCarService(std::array<car_variant,C>& cars) {
for (auto& c : cars) {
std::visit(
[](auto&& arg) {
arg->visitCarService( );
},c);
}
}
int main(int argc,char** argv) {
ICECar ice_car { };
ECar e_car { };
std::array<car_variant,2> cars {
std::make_shared<ICECar>(ice_car),std::make_shared<ECar>(e_car)
};
visitCarService(cars);
return 0;
}
这是使用 GCC 11 编译的,使用 std=c++17 和 -pedantic 集。据推测,它应该在 MS 下编译。 Here 是它的在线运行。