C++ 应用;使用;C++;纯虚函数
类B重写类A的纯虚拟函数“print()”。类C继承类B并具有“using A::print”语句。 为什么C类不是抽象类C++ 应用;使用;C++;纯虚函数,c++,overriding,virtual-functions,using-declaration,C++,Overriding,Virtual Functions,Using Declaration,类B重写类A的纯虚拟函数“print()”。类C继承类B并具有“using 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
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;
}
根据我第一次尝试寻找答案,@Oliv的评论和回答,让我尝试在
C
中使用::memberFct声明来总结的所有可能场景
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() {}
};
通过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
请注意,在类内使用声明的影响有限,即它们会更改名称查找,但不会更改虚拟分派(第一种情况)。成员函数是否为纯虚拟函数不会改变此行为。当基类函数被继承层次结构下的函数隐藏时(第二种情况),将调整查找,以使using声明的主题具有优先权。当基类函数被类本身的函数隐藏时(第三种情况),类本身的实现具有优先级,请参见
:
如果派生类已具有具有相同名称、参数列表和限定条件的成员,则派生类成员将隐藏或覆盖(不与)从基类引入的成员
在原始代码段中,C
因此不是抽象类,因为只有相关成员函数的查找机制受using声明的影响,vtable点并不指向纯虚拟成员函数实现。这是因为使用声明不会引入新成员或新定义。相反,它引入了一组声明,可以通过限定名称查找找到:
using声明中的每个using声明符,在using声明出现的声明区域中引入一组声明。using声明器引入的声明集是通过对using声明器中的名称执行限定名称查找([basic.lookup.qual]、[class.member.lookup])找到的,不包括如下所述隐藏的函数
它仅对通过限定名称查找找到的实体有影响。因此,它对最终替代者的定义没有影响:
[…]类对象S的虚拟成员函数C::vf是最终重写器,除非其中S是基类子对象(如果有)的最派生类([intro.object])声明或继承另一个重写vf的成员函数
其含义不同于:最终重写器是由表达式D::vf指定的实体,其中D是最派生的类,S是基类子对象
因此,它不影响类是否为抽象类:
如果类至少包含或继承一个最终重写器为纯虚拟的纯虚拟函数,则该类是抽象的
注1:
结果是using指令将导致非虚拟函数和虚拟函数[expr.call]的不同行为/3:
如果所选函数是非虚拟函数,或者如果类成员访问表达式中的id表达式是限定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;
}
A类{
公众:
作废打印(){
std::cout有点相关,因为a.那是错误的,派生类已经有了一个成员
,意味着这个类中首先声明的成员。请参阅这里的演示:@Oliv不确定我是否明白你的意思,为什么它的行为会根据所讨论的成员函数的虚拟性而有所不同?如果隐藏或重写这两者都会导致排除using声明引入的声明集,不应该表现出相同的行为吗?你的权利这不适用于这种情况。你所在的标准段落也不适用……我想我已经找到了解释。你怎么看?也许可以评论一下使用a::f
如何影响非虚拟的hid的查找den成员函数f
可能会有帮助吗?无论如何,伟大的标准挖掘!@lubgr我做到了,我给出的例子只是纯粹的,可能是令人困惑的奥术。啊,很抱歉没有弄清楚这一点,实际上我的意思是-如果A::print
是非虚拟的,那么在C
中使用A::print
确实会导致调用A::print
throughC
@lubgr aahhhhh!参见[namespace.udecl]/2“每个using声明都是一个声明[…]”,这与您的主张“因此using声明不是声明”直接矛盾。我想您想说的是,命名函数的using声明不是函数声明,等等。
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;
}
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;
}