请考虑以下事项:
struct A {
void f() { g(); }
private:
void g() {std::cout << "A.g" << std::endl;}
};
struct B: A {
// Inherited f from A, but I want f to call B.g this time.
private:
void g() {std::cout << "B.g" << std::endl;}
};
int main() {
B{}.f();
return 0;
}
此程序的输出为A.g
。有没有办法让它输出B.g
而不会使g
虚拟?
答案 0 :(得分:1)
如果你需要使用virtual
来避免运行时多态性,我认为事情必须更加冗长。
基本解决方案只是在f()
内重复B
的定义:
struct A {
void f() { g(); }
private:
void g() {std::cout << "A.g" << std::endl;}
};
struct B: A {
void f() { g(); } // will bind to B.g in this context
private:
void g() {std::cout << "B.g" << std::endl;}
};
但从软件工程的角度和DRY原则来看,这可能是不可取的。如果f()
中除了调用g()
之外还有一些功能,那么您的代码中将有两个该功能的副本,这很糟糕。
在大多数情况下,可以通过使用模板来避免多态性,但代码有点丑陋。这是实现这一目标的一种方法,其中成员函数g()
被放入结构体中,原始f()
被设为模板成员函数,并且有一个简单的重复定义{ {1}}为方便起见:
f()
答案 1 :(得分:1)
如果你需要保持你的呼叫模式(即,仅通过间接调用f()
来调用g()
),并且你需要运行时多态性,那么你基本上需要将f()声明为virtual
。毕竟,这是虚函数的确切用例。
如果你需要的只是编译时多态,你可以考虑使用Curiously Recurring Template Pattern,但是你可能从中获得的潜在性能增益是以代码可读性为代价的。除非您能够证明通过虚拟表进行间接调用的开销足够昂贵,以至于值得尝试比编译器更聪明,在我的书中获得可读性和正确性。
在极少数情况下,虚拟调用的运行时成本确实很重要,但除非您使用的环境要求您从代码中挤出最后一个纳秒(并且实际上需要非常非常少的字段),对你来说没关系。