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”的替代方法是什么?
答案 0 :(得分:1)
virtual
。标记函数virtual
的基础是通过派生类函数overidding具有多态行为。 好读:
When to mark a function in C++ as a virtual?
答案 1 :(得分:1)
是的,如果要显式调用特定类中的函数,可以使用完全限定名称。
b->A::fun();
这会调用属于fun()
的{{1}}版本。
答案 2 :(得分:1)
虚拟函数在所有派生类中都是虚拟的。没有办法阻止这种情况。
(§10.3/ 2 C ++ 11)如果虚拟成员函数vf在类Base和Derived中声明,直接或间接从Base派生,则成员函数vf具有相同的名称,参数类型声明了Base :: vf的-list(8.3.5),cv-qualification和ref-qualifier(或不存在相同的),然后Derived :: vf也是虚拟的(无论是否如此声明)并且它覆盖基地:: 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;
}
答案 3 :(得分:1)
以下内容可实现您所要求的可观察行为。在A
,非virtual
fun()
运行虚拟fun_()
,因此可以在B
中自定义行为,但任何人都可以在派生时调用fun()
class只会看到非多态的版本。
#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
虚拟的 fun
意味着在派生类中覆盖它是派生类的必要定制能力,但在派生层次结构中的某些点上,实现行为的选择可能已缩小到1并提供final
实施并非不合理。
我真正关心的是你隐藏 A
/ B
fun()
C::fun
...这就像他们做的那样令人不安不同的事情,那么你的代码可能很难推理或调试。 B
最终确定虚函数的决定意味着确定不需要进行这样的进一步定制。从A*
/ A&
/ B*
/ B&
开始工作的代码将执行一项操作,而在C
对象的类型静态知道的任何地方,行为可能会有所不同。模板化代码是一个可以在没有模板作者或用户非常清楚的情况下轻松调用C::fun
的地方。要评估这是否对您构成真正的危害,有助于了解“有趣”的功能目的是什么以及A
,B
和C
之间的实施方式可能会有所不同。 ..
答案 4 :(得分:0)
如果你在B中声明这个函数
void fun(int ignored=0);
它将成为一个不会参与解决虚拟呼叫的过载。请注意,即使a->fun()
实际引用A::fun()
,调用a
也会调用B
,所以我强烈建议不要使用此方法,因为它使事情变得更加混乱而不必要。
问题是:您想要实现或避免的究竟是什么?知道了,这里的人可以提出更好的方法。