我对c ++中的虚函数主题感到有点困惑。 是否有一个流程图总结了所有可能的情况?
例如:
class A {
public:
virtual void f(const int i) { cout << "A::f" << endl; }
};
class B : public A {
public:
// Hide A's f with new implementations
void f() { cout << "B::f" << endl; }
};
class C : public B {
public:
void f() { cout << "C::f" << endl; }
};
class D : public B { public:
void f(const int i) { cout << "D::f" << endl; }
};
void main() {
D d;
C c;
B* pb = &c;
pb->f();
A* pa = &d;
A* paa = &c;
pa->f(1);
paa->f(1); //in here C::f would be invoked?
}
在这种情况下,B
隐藏A::f
,C
覆盖B::f
并具有相同的签名。
pb->f()
会调用C::f
吗?
pa->f(1)
会调用A::f
吗?
我要求它知道B::f
是否仍然被认为是虚拟的,因此它的衍生类可以覆盖它,但它会隐藏A::f
。
以及如果C :: f默认为虚拟?
答案 0 :(得分:3)
A::f
和B::f
是两个不同的功能,即使它们具有相同的名称;由于B::f
未声明为virtual
,因此只有A::f
是虚拟的 - 并且没有人覆盖它。
pb->f()
使用指针的静态类型B*
来确定要调用的函数;这将是B::f
。
答案 1 :(得分:2)
pb->f()
会调用C::f
吗?
不,它不会。
我要求它知道
B::f
是否仍被视为virtual
,因此其派生类可以覆盖它,但它会隐藏A::f
。
B::f()
不是virtual
成员函数。
要使B::f()
成为虚拟成员函数,您必须使用:
class B : public A {
public:
// Hide A's f with new implementations
virtual void f() { cout << "B::f" << endl; }
};
<强>更新强>
您使用以下附加问题更新了帖子:
pa->f(1)
会调用A::f
吗?
是的,它会的。在pa
看到,B::f()
和C::f()
都不存在,只有A::f(int)
存在。
答案 2 :(得分:1)
是否有汇总所有可能情况的流程图?
完全一般,你需要知道C ++中的整套名称查找规则,遗憾的是这很复杂。
假设您只是想知道什么覆盖了什么,您可以在标准的几个段落中找到所有细节。
如果在类
vf
和类Base
中声明了虚拟成员函数Derived
,则直接或间接派生 从Base
,声明具有相同名称的成员函数vf,参数类型列表(8.3.5),cv-限定和ref {限定符(或不存在相同),Base::vf
,然后Derived::vf
也是虚拟的(无论是否是 如此声明)并且它覆盖Base::vf
。
...
即使析构函数不是继承的,派生类中的析构函数也会覆盖基类析构函数 声明虚拟;见12.4和12.5。
...
覆盖函数的返回类型应与被覆盖的返回类型相同 与函数类的函数或协变。如果函数
D::f
覆盖函数B::f
,则 如果函数的返回类型满足以下条件,则它们是协变的:
- 都是指向类的指针,两者都是对类的左值引用,或者两者都是对它们的右值引用 类
- 返回类型
中类的明确且可访问的直接或间接基类B::f
中的类与返回类型D::f
中的类相同,或者是 返回类型D::f
- 指针或引用在
D::f
的返回类型中具有相同的cv-qualification和类类型 与B::f
的返回类型中的类类型具有相同的cv资格或更少的cv资格。
由于B::f
与A::f
的参数类型不同,B::f
不会覆盖A::f
,出于同样的原因,C::f
不会覆盖A::f
。由于B::f
未声明为virtual
且,因此未覆盖A::f
,B::f
不是虚拟的。 C::f
不会覆盖B::f
,因为B::f
不是虚拟的。
由于B::f
不是虚拟的,pb->f()
始终在派生类中调用B::f
而不调用任何名为f
的函数。
答案 3 :(得分:0)
B::f
不覆盖A::f
,因为它们没有相同的参数类型列表,因此它也不是虚拟的,[class.virtual] / p2 :
如果在类
vf
和类Base
中声明虚拟成员函数Derived
,直接或间接地从Base
派生,则成员函数{{1声明具有相同名称, parameter-type-list (8.3.5),cv-qualification和ref-qualifier(或缺少相同),{}} { {1}}也是虚拟的(无论是否如此声明)并且覆盖vf
。
因此,派生类方法可以隐式虚拟,无论它是否具有Base::vf
说明符,只有它匹配基类方法的某些属性。在这种情况下,Derived::vf
不是虚拟的,因此Base::vf
使用指针的静态类型virtual
,并调用B::f
而非pb->f()
。