2
struct A{
    virtual void fun(){cout<<"A";}
};
struct B:public A{
    void fun(){cout<<"B";}
};
struct C:public B{
    void fun(){cout<<"C";}
};
int main() 
{
    C c;B b1;   
    A *a=&b1;
    a->fun(); //1
    B *b=&c;
    b->fun(); //2
    return 0;
}

在上面的代码中,B::fun() 隐式转换为虚函数,因为我将 A::fun() 设为虚拟。我可以停止这种转换吗?

如果不可能,使上述代码打印“BB”的替代方法是什么?

4

5 回答 5

1

虚函数在所有派生类中都是虚函数。没有办法阻止这种情况。

(§10.3/2 C++11) 如果虚成员函数 vf 在类 Base 和 Derived 类中声明,直接或间接从 Base 派生,则具有相同名称的成员函数 vf,参数类型列表 ( 8.3.5)、cv-qualification 和 ref-qualifier(或没有相同)作为 Base::vf 被声明,然后 Derived::vf 也是虚拟的(无论它是否如此声明)并且它覆盖 Base:: VF。为方便起见,我们说任何虚函数都会覆盖自身。

但是,如果您想使用对应于指针的static类型而不是dynamic类型的函数(即,在您的示例中,B::fun而不是C::fun,假设指针被声明为B*),那么您可以在至少在 C++11 中,使用下面的别名定义来访问静态(=编译时)类型:

template <typename Ptr>
using static_type = typename std::remove_pointer<Ptr>::type;

这就是您在main()(或其他任何地方)使用它的方式:

int main() 
{
  C c; B b1;   

  A *a = &b1;
  a->fun();

  B *b = &c;

  /* This will output 'B': */    
  b->static_type<decltype(b)>::fun();

  return 0;
}
于 2013-07-02T05:48:10.060 回答
1

以下实现了您要求的可观察行为。在A, non - virtual fun()run virtualfun_()因此可以在 中自定义行为B,但是任何调用fun()派生类的人都只会看到非多态版本。

#include <iostream>
using namespace std;

struct A{
    void fun(){fun_();}
  private:
    virtual void fun_() { cout << "A\n"; }
};

struct B:public A{
    void fun(){cout<<"B\n";}
  private:
    virtual void fun_() final { fun(); }
};

struct C:public B{
    void fun(){cout<<"C\n";}
};

int main()
{
    C c;B b1;
    A *a=&b1;
    a->fun(); //1
    B *b=&c;
    b->fun(); //2
    c.fun();    // notice that this outputs "C" which I think is what you want
}

如果使用 C++03,您可以简单地省略“final”关键字 - 它只是为了防止在B派生类中进一步不必要地覆盖虚拟行为,例如C.

(您可能会发现将此与“非虚拟接口模式”进行对比很有趣 - 请参阅 Sutter 和 Alexandrescu 的 C++ 编码标准,第 39 点)

讨论

A拥有funvirtual 意味着在派生类中覆盖它是派生类的必要定制能力,但在派生层次结构中的某个点,实现行为的选择可能已缩小到 1 并提供final实现并非不合理。

我真正担心的是你隐藏 A/ ......这很令人不安,好像他们做了不同的事情,那么你的代码可能很难推理或调试。 最终确定虚拟功能的决定意味着确定没有必要进行这种进一步的定制。从// /开始工作的代码会做一件事,而在静态已知对象类型的地方,行为可能会有所不同。模板化代码是一个很容易被调用的地方,而模板作者或用户并没有意识到这一点。为了评估这对您来说是否是真正的危险,这将有助于了解“乐趣”的功能目的是什么,以及实现之间可能有何不同,Bfun()C::funBA*A&B*B&CC::funABC....

于 2013-07-02T05:58:22.967 回答
1
  • 如果您不希望派生类覆盖该函数,那么没有理由virtual在基类中标记它。标记函数的基础virtual是通过派生类函数覆盖具有多态行为。

好读:
何时将 C++ 中的函数标记为虚拟函数?

  • 如果您希望您的代码保护您免受派生类中的意外覆盖。您可以在 C++11 中使用final 说明符。
于 2013-07-02T05:37:38.873 回答
1

是的,如果您想显式调用特定类中的函数,您可以使用完全限定名称。

b->A::fun();

这将调用fun()属于的版本A

于 2013-07-02T05:38:30.090 回答
0

如果你像这样在 B 中声明函数

void fun(int ignored=0);

它将成为一个重载,不会参与解决虚拟呼叫。请注意,即使实际上指的是 a ,调用a->fun()也会调用,因此我强烈建议不要使用这种方法,因为它会使事情变得比必要的更加混乱。A::fun()aB

问题是:您想要实现或避免的究竟是什么?知道这一点,这里的人们可以提出更好的方法。

于 2013-07-02T05:49:05.940 回答