C++ 应用;使用;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

类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 : 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
through
C
@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;               
  }