我使用继承只是为了代码重用,我从不将类强制转换为它的基类。这是一个性能很重的程序,所以我想避免使用 virtual
函数,但我不知道该怎么做。考虑以下代码
class A{
public:
void print(){
std::cout << "Result of f() is: " << f() << std::endl;
}
virtual std::string f(){
return "A";
}
};
class B : public A{
public:
virtual std::string f(){
return "B";
}
};
是否有可能不对函数 f()
使用 virtual
函数并且不在类中重新实现函数 print()
B
?我不关心 A
是 B
的基类,我只是不想再写下 f()
了。可能继承不是要走的路,也许模板可以以聪明的方式使用,但我不知道。
最佳答案
CRTP pattern当可以静态确定为特定方法调用什么实现方法时,通常用于避免动态分派(dispatch)。
在你的例子中,A
和 B
将从单个基类继承,该基类提供 print()
方法。基类,我们称它为Print
, 是一个模板,其模板参数是一个提供 f()
的类.为这种模式赢得“奇特”绰号的转折是子类必须继承基类在子类上模板化。这允许子类访问基类的 print
方法,但获得基类的版本 - 并通过扩展获得 print
的版本- 调用他们自己的 f
.
这是一个工作代码的例子:
#include <iostream>
template<typename F>
class Print {
public:
void print() {
F& final = static_cast<F&>(*this);
std::cout << "Result of f() is: " << final.f() << std::endl;
}
};
class A: public Print<A> {
public:
std::string f(){
return "A";
}
};
class B: public Print<B> {
public:
std::string f(){
return "B";
}
};
int main() {
A a;
B b;
a.print();
b.print();
}
尽管 print
在 A
中重用实现和 B
,这里没有虚拟方法,也没有虚拟(运行时)分派(dispatch)或运行时检查。唯一的 Actor 礼物是static_cast<>
其安全性由编译器适当验证。
这是可能的,因为每次使用 Print<F>
, 编译器确切地知道 F
是什么是。因此 Print<A>::print
已知会调用 A::f
, 而 Print<B>::print
调用 B::f
,在编译时都知道。这使编译器能够像任何其他非虚拟方法调用一样内联和以其他方式优化此类调用。
缺点是也没有继承。注意 B
不是来自 A
- 如果是,该模式将无法正常工作,并且 A::print
和 B::print
会打印 A
,因为这就是Print<A>
输出。更根本的是,你不能传递 B*
哪里有A*
预期 - 这是未定义的行为。事实上,A
和 B
不共享任何公共(public)父类(super class),Parent<A>
和 Parent<B>
类是完全分开的。失去运行时分派(dispatch)及其优点和缺点,并改为启用静态分派(dispatch),是静态多态性的基本权衡。
关于c++ - 为代码重用而不必要地使用虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42071790/