虚拟方法 C++
Posted
技术标签:
【中文标题】虚拟方法 C++【英文标题】:virtual methods C++ 【发布时间】:2015-01-15 20:40:54 【问题描述】:我面临以下问题。
我实现了父类 - Vehicle,它有一些派生类,其中之一 - FastVehicle。
在程序中,我需要存储一个 Vehicle* 指针向量。指针可能指向 Vehicle 对象或 FastVehicle 对象。
1) 我希望能够为向量中的每个对象调用 print() 方法。 问题是,在 FastVehicle 的情况下,我还想将参数传递给函数, 我需要调用一个带有签名的函数:
void print(int a)
我对虚函数机制略知一二,但据我所知,它只有在两个函数具有相同签名的情况下才有效。
我想听听关于如何解决的建议。
2) 另外在派生类FastVehicle 中还有一些独特的功能,它不与父类Vehicle 共享。 它执行的任务应该只对 FastVehicle 对象执行。 实现这一目标的最干净的方法是什么? 我想也许可以在父类 Vehicle 中实现“空”虚函数,并在 FastVehicle
的覆盖方法中实现“真实”任务也许有人可以提出更好的解决方案。
谢谢
【问题讨论】:
要么实现通用接口,要么使用dynamic_cast。dynamic_cast
是如果您真的希望派生类具有不兼容的接口的答案。如果可以的话,定义一个通用接口会更简洁。
不得不使用dynamic_cast
通常表明你考虑得不够充分。
您应该首先考虑您的设计。 FastVehicle 能做哪些车辆不能做的事情?如果它只是跑得更快,那么 Vehicle 应该有一个 speed 成员,而更快的车辆只是将它设置得更高。
一个类似的问题 - 也涉及汽车 :) - 可以在这里找到:***.com/questions/7200446/… IMO,这是一个完全合法的向下转换用例。
【参考方案1】:
您始终可以使用 dynamic_cast 将 Vehicle 转换为 FastVehicle。如果 Vehicle 不是 FastVehicle,则返回 NULL。是否真的应该这样做,取决于您的使用情况。
for(Vehicle* vehicle : vehicleVector)
FastVehicle* fastVehicle = dynamic_cast<FastVehicle*>(vehicle);
if(fastVehicle)
fastVehicle->print(1337);
fastVehicle->somethingElse();
else
vehicle->print();
此处提供完整示例:https://ideone.com/69n6Jb
【讨论】:
感谢您的回答。父类是否需要至少有一个虚函数才能执行dynamic_cast? @Day_Dreamer 是的。 不要认为那行得通。我认为 dynamic_cast 横向投射(在兄弟姐妹之间)就好了,所以你永远不会碰到 else。【参考方案2】:您很可能必须重新考虑为什么需要 FastVehicle 的参数,而不是其他类型的车辆。对我来说,这表明设计不好。
只需在基类中声明 print(int),覆盖它,但在不需要 int 的类中,忽略它。
【讨论】:
【参考方案3】:务实的解决方案是:
将int a
参数传递给虚拟print
方法,但在Vehicle
中忽略它,只在FastVehicle
中使用它
按照您的建议,只需向基类添加一个“空”虚函数,该函数在 Vehicle
中是无操作的,并且仅在 FastVehicle
中实现
例如:
struct Vehicle
virtual ~Vehicle()
virtual void print(int /*a*/) const std::cout << "Vehicle print\n";
virtual void somethingElse() /* no-op */
;
struct FastVehicle : Vehicle
void print(int a) const override std::cout << "FastVehicle print " << a << "\n";
void somethingElse() override std::cout << "Something else!\n";
;
for (auto vehicle : vehicles)
vehicle->print(512);
vehicle->somethingElse();
Live demo
【讨论】:
【参考方案4】:也许你可以用抽象vehicleI
重构:
struct vehicleI
....
virtual void print(int) = 0;
然后是你的vehicle
:
struct vehicle : vehicleI
....
void print(int i = 0);
还有你的fastVehicle
:
struct fastvehicle: vehicleI
....
void print(int);
【讨论】:
请注意,如果您在vehicleI
指针上调用 print,则不会使用 i=0
默认参数,因为默认参数基于静态类型。就个人而言,我会完全避免使用默认参数以避免混淆。
@ChrisDrew 好点,应该是 printf
并接受可变数量的参数。或者成为某种流......【参考方案5】:
如果您想正确使用对 Vehicle 接口的动态调用,您需要定义一个通用接口。 如果您需要在 FastVehicle 的情况下指定参数,而在 FastVehicle 的情况下不需要指定参数,则不再是接口。
您有两种解决方案:
默认参数
struct Vehicle
virtual void print(int a=0) ;
;
struct FastVehicle : public Vehicle
void print(int a=0) override ;
;
现在您可以使用或不使用参数来调用。
第二个选项:
struct Vehicle
virtual void print() ;
;
struct FastVehicle : public Vehicle
void print() override ;
void setA(int a) _a = a;
_a;
;
现在您可以通过其他方法设置“a”变量,但不能通过 Vehicle 的接口访问对象时设置。
【讨论】:
以上是关于虚拟方法 C++的主要内容,如果未能解决你的问题,请参考以下文章
调用非虚拟基方法时,C++ 中的虚拟继承是不是有任何惩罚/成本?