如何避免 C++17 中的虚拟继承?
How to avoid virtual inheritance in C++17?
让我们看看示例 classes。基础 class 是 ITransport
,传输 class 接口:
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_;
};
我们也假设我们想要创建一个自动移动的交通工具 class:
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.
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_;
};
这个逻辑的延续可以是,例如,添加火车class等等:
template <typename EnergySource>
class Train : public SelfMovingTransport<EnergySource> {
/*Not interesting.*/
};
我使用 c++17
编译器 (MS)。不多也不少。
我想创建一个数组(或 std::vector<Car*>
)
指向不同类型汽车的指针和
为他们调用一些常用的方法。
例如,有一个简单的方法将它们全部发送到
服务(参见 Car::visitCarServeice()
)。
我试过树的想法:
- 创建 classes
ISelfMovingTransport
和 ICar
:
class ISelfMovingTransport : public virtual ITransport {
/*All the same.*/
};
class ICar : public virtual ISelfMovingTransport {
/*All the same.*/
};
Changed Transprot
to:
class Transport : public virtual ITransport {
/* All the same. */
}
Changed SelfMovingTransport
to:
template <typename EnergySource>
class SelfMovingTransport : public ISelfMovingTransport,
public Transport<EnergySource> {};
Changed Car
to:
template <typename EnergySource>
class Car: public ICar, public SelfMovingTransport<EnergySource> {
/*All the same*/
};
In the end solution did not work, because static_cast
can not be used to cast pointer to virtually derived
class pointer (See pastebin link.).
Example code can't be compiled (error: cannot convert from pointer to base class ‘ISelfMovingTransport’ to pointer to derived class ‘ElectricCar’ because the base is virtual).
When I want to make actions with ElectricCar
which is
accessed as a pointer to a Car
, I need
dynamic_cast<ElectricCar*>(carPtr)
where carPtr
is of Car*
.
But dynamic_cast
is not allowed, RTTI
is turned off.
- 使用
std::vector<Transport*>
并将对象投射到 Car
。
它有效,但我不喜欢这个解决方案,因为很难检查是否一切正常
正确。
- 使用
std::variant<ICECar, ElectricCar>
和 std::visit
。
(std::visit([](auto& car) -> void { car.visitCarServeice(); }, carV)
)。
(现在用这个方法实现。)。
在这个例子中(代表真实项目中的问题)我不想改变逻辑(尤其是classes从Transport
级别到Car
级别)。
在没有 RTTI 和 dynamic_cast 的情况下,是否有一种通用的方法来完成所需的事情?
是std::variant
'OK'这种情况(假设车class是不
大小不同 and/or 内存不重要)?
问的问题,因为不知道怎么google那个。
P.S。所有示例都是真实项目中情况的表示(模拟等)。我请你想象一下,能量类型作为参数是真正需要的,而不是考虑复杂性(混合动力汽车等)。
P.P.S。在实际项目中我需要一个“汽车”的唯一对象作为其他class.
的字段
I want to create an array of cars of different types.
你不能。数组在 C++ 中是同构的。所有元素始终具有相同的类型。
Use std::vector<Transport*>
and cast objects to Car
如果矢量元素应该只指向汽车,那么使用指向 ICar
的指针似乎更有意义。此外,如果向量应该拥有指向的对象,那么您应该使用智能指针。并且您需要将析构函数设为虚拟。
Is there a common way to do required things without RTTI and dynamic_cast?
通常是:虚拟函数。
Is std::variant 'OK' in this situation
可以,如果变体的数量是一个小常量。相比之下,继承层次结构允许添加任意多个子类。
我相信这解决了您的问题,但很难说。它去除了代码中的一些细节,但演示了该技术。它使用 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 是它的在线 运行。
让我们看看示例 classes。基础 class 是 ITransport
,传输 class 接口:
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_;
};
我们也假设我们想要创建一个自动移动的交通工具 class:
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.
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_;
};
这个逻辑的延续可以是,例如,添加火车class等等:
template <typename EnergySource>
class Train : public SelfMovingTransport<EnergySource> {
/*Not interesting.*/
};
我使用 c++17
编译器 (MS)。不多也不少。
我想创建一个数组(或 std::vector<Car*>
)
指向不同类型汽车的指针和
为他们调用一些常用的方法。
例如,有一个简单的方法将它们全部发送到
服务(参见 Car::visitCarServeice()
)。
我试过树的想法:
- 创建 classes
ISelfMovingTransport
和ICar
:
class ISelfMovingTransport : public virtual ITransport { /*All the same.*/ }; class ICar : public virtual ISelfMovingTransport { /*All the same.*/ };
Changed
Transprot
to:class Transport : public virtual ITransport { /* All the same. */ }
Changed
SelfMovingTransport
to:template <typename EnergySource> class SelfMovingTransport : public ISelfMovingTransport, public Transport<EnergySource> {};
Changed
Car
to:template <typename EnergySource> class Car: public ICar, public SelfMovingTransport<EnergySource> { /*All the same*/ };
In the end solution did not work, because
static_cast
can not be used to cast pointer to virtually derived class pointer (See pastebin link.). Example code can't be compiled (error: cannot convert from pointer to base class ‘ISelfMovingTransport’ to pointer to derived class ‘ElectricCar’ because the base is virtual). When I want to make actions withElectricCar
which is accessed as a pointer to aCar
, I needdynamic_cast<ElectricCar*>(carPtr)
wherecarPtr
is ofCar*
. Butdynamic_cast
is not allowed,RTTI
is turned off.
- 使用
std::vector<Transport*>
并将对象投射到Car
。 它有效,但我不喜欢这个解决方案,因为很难检查是否一切正常 正确。 - 使用
std::variant<ICECar, ElectricCar>
和std::visit
。 (std::visit([](auto& car) -> void { car.visitCarServeice(); }, carV)
)。 (现在用这个方法实现。)。
在这个例子中(代表真实项目中的问题)我不想改变逻辑(尤其是classes从Transport
级别到Car
级别)。
在没有 RTTI 和 dynamic_cast 的情况下,是否有一种通用的方法来完成所需的事情?
是std::variant
'OK'这种情况(假设车class是不
大小不同 and/or 内存不重要)?
问的问题,因为不知道怎么google那个。
P.S。所有示例都是真实项目中情况的表示(模拟等)。我请你想象一下,能量类型作为参数是真正需要的,而不是考虑复杂性(混合动力汽车等)。
P.P.S。在实际项目中我需要一个“汽车”的唯一对象作为其他class.
的字段I want to create an array of cars of different types.
你不能。数组在 C++ 中是同构的。所有元素始终具有相同的类型。
Use
std::vector<Transport*>
and cast objects to Car
如果矢量元素应该只指向汽车,那么使用指向 ICar
的指针似乎更有意义。此外,如果向量应该拥有指向的对象,那么您应该使用智能指针。并且您需要将析构函数设为虚拟。
Is there a common way to do required things without RTTI and dynamic_cast?
通常是:虚拟函数。
Is std::variant 'OK' in this situation
可以,如果变体的数量是一个小常量。相比之下,继承层次结构允许添加任意多个子类。
我相信这解决了您的问题,但很难说。它去除了代码中的一些细节,但演示了该技术。它使用 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 是它的在线 运行。