如何正确隐藏方法

时间:2016-06-30 13:48:25

标签: c++

请考虑以下事项:

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虚拟?

2 个答案:

答案 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,但是你可能从中获得的潜在性能增益是以代码可读性为代价的。除非您能够证明通过虚拟表进行间接调用的开销足够昂贵,以至于值得尝试比编译器更聪明,在我的书中获得可读性和正确性。

在极少数情况下,虚拟调用的运行时成本确实很重要,但除非您使用的环境要求您从代码中挤出最后一个纳秒(并且实际上需要非常非常少的字段),对你来说没关系。