Gtr*_*rex 15 c++ overriding virtual-functions using-declaration
B类覆盖了A类的纯虚函数"print()".C类继承了B类,并且具有"使用A :: print"语句.既然C类不是抽象类呢?
class A {
public :
virtual void print() =0;
};
class B:public A {
public:
void print();
};
void B :: print() {
cout << "\nClass B print ()";
}
class C : public B {
public:
using A::print;
};
void funca (A *a) {
// a->print(1);
}
void funcb (B *b) {
b->print();
}
void funcc (C *c) {
c->print();
}
int main() {
B b;
C c;
funca(&c);
funcb(&c);
funcc(&c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
Class B print ()
Class B print ()
Run Code Online (Sandbox Code Playgroud)
基于我第一次尝试找到答案,@ Oliv的评论和回答,让我试着总结一下using A::memberFct
内部声明的所有可能场景C
.
A
的成员函数是虚拟的并被覆盖 B
A
的成员函数是非虚拟的并且被隐藏 B
A
的成员函数是非虚拟的并且C
本身是隐藏的这些案例的一个小例子如下.
struct A {
virtual void f() {}
void g() {}
void h() {}
};
struct B : A {
void f() override {}
void g() {}
};
struct C : B {
using A::f; // Virtual function, vtable decides which one is called
using A::g; // A::g was hidden by B::g, but now brought to foreground
using A::h; // A::h is still hidden by C's own implementation
void h() {}
};
Run Code Online (Sandbox Code Playgroud)
通过C
接口调用所有三个函数会导致不同的函数调用:
C{}.f(); // calls B::f through vtable
C{}.g(); // calls A::g because of using declarative
C{}.h(); // calls C::h, which has priority over A::h
Run Code Online (Sandbox Code Playgroud)
请注意,在类中使用声明的影响有限,即它们更改名称查找,但不更改虚拟调度(第一种情况).成员函数是否为纯虚函数不会更改此行为.当继承层次结构中的函数隐藏基类函数时(第二种情况),将调整查找,使得使用声明的那个具有优先权.当类本身的函数隐藏基类函数时(第三种情况),类本身的实现具有优先权,请参阅 cppreference:
如果派生类已具有具有相同名称,参数列表和限定条件的成员,则派生类成员将隐藏或覆盖(不冲突)从基类引入的成员.
在原始片段中,C
因此不是抽象类,因为只有所讨论的成员函数的查找机制受使用声明的影响,并且vtable点不指向纯虚拟成员函数实现.
这是因为using声明不是声明[namespace.udecl]/1,而是它引入了一组声明,可以通过限定名称查找:
using声明中的每个using-declarator都会在声明区域中引入一组声明,其中出现using声明.using-declarator引入的声明集是通过对using-declarator中的名称执行限定名称查找([basic.lookup.qual],[class.member.lookup])来找到的,不包括所描述的隐藏的函数下面.
所以使用声明不是声明.它只对通过限定名称查找找到的实体有影响.因此,它对最终覆盖 的定义没有影响[class.virtual]/2:
[...]类对象S的虚拟成员函数C :: vf是最终覆盖,除非S是基类子对象(如果有)的最派生类([intro.object])声明或继承另一个覆盖vf的成员函数.
其含义不同于:最终覆盖是由表达式D :: vf指定的实体,其中D是派生类最多的类,其中S是基类子对象.
因此,它不会影响一个类是否是一个抽象类[class.abstract]/4:
如果一个类包含或继承至少一个最终覆盖为纯虚拟的纯虚函数,则该类是抽象的.
注1:
结果是using指令将导致非虚函数和虚函数[expr.call]/3的行为不同:
如果所选函数是非虚拟的,或者类成员访问表达式中的id-expression是qualified-id,则调用该函数.否则,调用它在对象表达式的动态类型中的最终覆盖; 这种调用称为虚函数调用.
只是:
所以,如果print
不是虚拟的:
class A {
public :
void print() {
std::cout << "\n Class A::print()";
}
};
int main() {
B b;
C c;
b.print() // Class B print ()
c.print() // Class A print ()
//Equivalent to:
c.C::print() // Class A::print()
return 0;
}
Run Code Online (Sandbox Code Playgroud)
笔记2:
正如一些人在前面的标准段落中可能已经注意到的那样,可以对虚拟函数进行合格调用以获得非虚拟行为.所以虚拟函数的使用声明可能是实用的(可能是一个不好的做法):
class A {
public :
virtual void print() =0;
};
//Warning arcane: A definition can be provided for pure virtual function
//which is only callable throw qualified name look up. Usualy an attempt
//to call a pure virtual function through qualified name look-up result
//in a link time error (that error message is welcome).
void A::print(){
std::cout << "pure virtual A::print() called!!" << std::endl;
}
int main() {
B b;
C c;
b.print() // Class B print ()
c.print() // Class B print ()
c.C::print() // pure virtual A::print() called!!
//whitout the using declaration this last call would have print "Class B print()"
return 0;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
614 次 |
最近记录: |