How to stop implicit conversion to virtual function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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"?
以下是你所要求的可观察行为。在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include <iostream> using namespace std; struct A{ void fun(){fun_();} private: virtual void fun_() { cout <<"A "; } }; struct B:public A{ void fun(){cout<<"B ";} private: virtual void fun_() final { fun(); } }; 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 c.fun(); // notice that this outputs"C" which I think is what you want } |
如果使用C++ 03,你可以简单地省掉"最终"关键字——它只是为了防止EDOCX1和11个派生类(如EDCOX1×19)中的虚拟行为的进一步不必要的重写。
(您可能会发现将此与"非虚拟接口模式"进行对比是有趣的——参见萨特和Alexandrescu的C++编码标准,第39点)
讨论
拥有
我真正担心的是你把
虚拟函数在所有派生类中都是虚拟的。没有办法阻止这种情况。
(§10.3/2 C++11) If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf. For convenience we say that any virtual function overrides itself.
但是,如果您希望使用对应于静态指针(而不是动态类型)的函数(即,在您的示例中,EDCOX1×4)代替EDCOX1(5),假定指针被声明为EDCOX1(6)),那么您至少可以在C++ 11中使用下面的别名定义来访问静态(=编译时)类型:
1 2 | template <typename Ptr> using static_type = typename std::remove_pointer<Ptr>::type; |
这是您在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
是的,如果要显式调用特定类中的函数,可以使用完全限定名。
1 | b->A::fun(); |
这将调用属于
- 如果不希望派生类重写函数,那么就没有理由在基类中标记它
virtual 。标记一个函数virtual 的基础是通过派生类函数overidding来产生多态行为。
良好阅读:何时将C++中的函数标记为虚拟?
- 如果您希望代码防止您在派生类中意外地覆盖,则可以使用C++ 11中的最终说明符。
如果你在B中这样声明函数
1 | void fun(int ignored=0); |
它将成为不参与解析虚拟调用的重载。注意,调用
问题是:你到底想要实现或避免什么?知道了这一点,这里的人们可以提出一个更好的方法。