基类能否知道派生类是否已重写虚拟方法
Can a base class know if a derived class has overridden a virtual method?
同样的问题也存在于C#上,但不适用于C++。
class Base
{
void dispatch()
{
if (newStyleHasBeenOverridden()) //how to find this out?
newStyle(42);
else
oldStyle(1, 2);
}
virtual void oldStyle(int, int) { throw "Implement me!"; }
virtual void newStyle(int) { throw "Implement me!"; }
}
class Derived:public Base
{
void newStyle(int) override
{
std::cout<<"Success!";
}
}
警告:此解决方案不是跨平台的,因为它依赖于GCC扩展和一些未定义的行为。
GCC 允许语法通过说this->*&ClassName::functionName
来从this
的 vtable 中获取指向函数的指针。实际使用它可能不是一个好主意,但无论如何,这里有一个演示:
#include <iostream>
class Base {
public:
void foo() {
auto base_bar_addr = reinterpret_cast<void*>(&Base::bar);
auto this_bar_addr = reinterpret_cast<void*>(this->*&Base::bar);
std::cout << (base_bar_addr == this_bar_addr ? "not overridden" : "overridden") << std::endl;
}
virtual void bar() { };
};
class Regular : public Base { };
class Overriding : public Base {
public:
virtual void bar() { };
};
int main() {
Regular r;
r.foo();
Overriding o;
o.foo();
}
对于后代:
- ICC允许语法,但它有不同的含义,这与只说
&Base::bar
相同,所以你总是认为它没有被覆盖。 - Clang和MSVC完全拒绝了代码。
这是一个设计问题。
但是,为了回答实际问题,有几种方法可以在不重新设计的情况下完成此操作(但实际上,您应该重新设计它)。
一个(可怕的)选择是调用 newstyle 方法并捕获未重写时发生的异常。
void dispatch() {
try {
newStyle(42);
} catch (const char *) {
oldStyle(1, 2);
}
}
如果 newStyle 已被重写,则将调用该重写。 否则,基本实现将抛出,调度将捕获,然后回退到oldStyle。 这是对异常的滥用,性能会很差。
另一种(稍微不那么可怕)的方法是使newStyle的基本实现向前发展到oldStyle。
void dispatch() {
newStyle(42);
}
virtual void newStyle(int) { oldStyle(1, 2); }
virtual void oldStyle(int, int) { throw "implement me"; }
这至少朝着更好的设计方向发展。 继承的要点是允许高级代码能够互换使用对象,而不管其专用性如何。 如果调度必须检查实际对象类型,那么您违反了 Liskov 替换原则。 调度应该能够以相同的方式处理所有对象,并且行为的任何差异都应该来自重写方法本身(而不是重写的存在)。
更简单的是,调度决策由Derived
类完成。 摘要Base
类基本上只是一个"接口",Derived
类应该在其中实现所有virtual
函数。
这个问题听起来也像XY问题。
我以为这就是你想要的:
class Base // abstract class
{
virtual void oldStyle(int, int) = 0; // pure virtual functions
virtual void newStyle(int) = 0; // needs to be implemented
};
class Derived:public Base
{
public:
Derived(bool useNewStyle): _useNewStyle(useNewStyle) {}
void newStyle(int) { std::cout << "new style"; }
void oldStyle(int, int) { std::cout << "old style"; }
void dispatch()
{
if (_useNewStyle) {
newStyle(42);
return;
}
oldStyle(1, 2);
return;
}
private:
bool _useNewStyle = false;
};
Derived d(true); // use new style
d.dispatch(); // "new style"
相关文章:
- 是否总是可以将使用递归编写的程序重写为不使用递归的程序C++,性能观点是什么?
- 基类能否知道派生类是否已重写虚拟方法
- C++是否可以将宏重写浮点文本为双文本或至少删除 f 后缀?
- 检查子类是否执行了方法重写
- 是否有可能有一个派生类继承最终函数但创建相同的函数(而不是重写)
- 是否可以在不重写派生类中记录虚拟成员?
- 是否有必要重写将基类作为参数的虚函数
- 我是否应该重写循环标头以迭代 STL 向量
- 方法重写(没有虚拟方法或指针)是否被认为是多态性的一部分
- 是否可以将其重写为一行
- 是否有可能有一个基类方法来调用它的所有派生类的相同(但被重写的方法)?
- 是否可以重写MyClass*(而不是MyClass)的析构函数
- 是否有方法将所有赋值运算符(+=、*=等)转发为隐式使用重写的直接赋值运算符(=)
- 是否应该初始化具有未重写的纯虚拟析构函数的类
- 是否可以重写std::endl
- 是否存在一个尊重重写的new/delete的C++分配器
- 如果用c++重写C项目的部分代码,但遗漏了相同的API,是否存在ABI兼容性问题?
- C++中的方法重写是否总是在派生类的标头中完成?
- c++中父模板类的重写方法是否可以调用父模板类的重写方法?
- 是否有可能重写特定父类的虚函数