覆盖虚拟功能和隐藏非虚拟功能有什么区别?

Jed*_*aaf 15 c++ inheritance overriding virtual-functions method-hiding

给定以下代码片段,函数调用有何不同?什么是隐藏功能?什么是功能重写?它们如何与函数重载相关?两者有什么区别?我在一个地方找不到这些的好描述,所以我在这里问我所以我可以巩固这些信息.

class Parent {
  public:
    void doA() { cout << "doA in Parent" << endl; }
    virtual void doB() { cout << "doB in Parent" << endl; }
};

class Child : public Parent {
  public:
    void doA() { cout << "doA in Child" << endl; }
    void doB() { cout << "doB in Child" << endl; }
};

Parent* p1 = new Parent();
Parent* p2 = new Child();
Child* cp = new Child();

void testStuff() {
  p1->doA();
  p2->doA();
  cp->doA();

  p1->doB();
  p2->doB();
  cp->doB();
}
Run Code Online (Sandbox Code Playgroud)

dyp*_*dyp 24

什么是隐藏功能?

......是隐藏名称的一种形式.一个简单的例子:

void foo(int);
namespace X
{
    void foo();

    void bar()
    {
        foo(42); // will not find `::foo`
        // because `X::foo` hides it
    }
}
Run Code Online (Sandbox Code Playgroud)

这也适用于基类中的名称查找:

class Base
{
public:
    void foo(int);
};

class Derived : public Base
{
public:
    void foo();
    void bar()
    {
        foo(42); // will not find `Base::foo`
        // because `Derived::foo` hides it
    }
};
Run Code Online (Sandbox Code Playgroud)

什么是功能重写?

这与虚函数的概念有关.[class.virtual]/2

如果虚拟成员函数vf在类Base和类中声明Derived,直接或间接地从具有相同名称Base的成员函数派生vf,参数类型列表,cv限定和ref-qualifier(或不存在相同)Base::vf声明,然后Derived::vf也是虚拟的(无论是否声明)并覆盖Base::vf.

class Base
{
private:
    virtual void vf(int) const &&;
    virtual void vf2(int);
    virtual Base* vf3(int);
};

class Derived : public Base
{
public: // accessibility doesn't matter!
    void vf(int) const &&; // overrides `Base::vf(int) const &&`
    void vf2(/*int*/);     // does NOT override `Base::vf2`
    Derived* vf3(int);     // DOES override `Base::vf3` (covariant return type)
};
Run Code Online (Sandbox Code Playgroud)

调用虚函数时,最终的覆盖变得相关:[class.virtual]/2

除非最大派生类是基类子对象(如果有)声明或继承覆盖的另一个成员函数,否则C::vf类对象的虚拟成员函数S是最终S覆盖vf.

即如果你有一个类型的对象S,最后的覆盖是你在遍历S返回其基类的类层次结构时看到的第一个覆盖.重要的是,函数调用表达式的动态类型用于确定最终的覆盖:

Base* p = new Derived;
p -> vf();    // dynamic type of `*p` is `Derived`

Base& b = *p;
b  . vf();    // dynamic type of `b` is `Derived`
Run Code Online (Sandbox Code Playgroud)

覆盖和隐藏有什么区别?

实质上,基类中的函数总是被派生类中的同名函数隐藏; 无论派生类中的函数是否覆盖基类的虚函数:

class Base
{
private:
    virtual void vf(int);
    virtual void vf2(int);
};

class Derived : public Base
{
public:
    void vf();     // doesn't override, but hides `Base::vf(int)`
    void vf2(int); // overrides and hides `Base::vf2(int)`
};
Run Code Online (Sandbox Code Playgroud)

要查找函数名称,请使用表达式的静态类型:

Derived d;
d.vf(42);   // `vf` is found as `Derived::vf()`, this call is ill-formed
            // (too many arguments)
Run Code Online (Sandbox Code Playgroud)

它们如何与函数重载相关?

由于"函数隐藏"是一种名称隐藏形式,如果隐藏了函数的名称,则所有重载都会受到影响:

class Base
{
private:
    virtual void vf(int);
    virtual void vf(double);
};

class Derived : public Base
{
public:
    void vf();     // hides `Base::vf(int)` and `Base::vf(double)`
};
Run Code Online (Sandbox Code Playgroud)

对于函数重写,只覆盖具有相同参数的基类中的函数; 你当然可以重载一个虚函数:

class Base
{
private:
    virtual void vf(int);
    virtual void vf(double);
    void vf(char);  // will be hidden by overrides in a derived class
};

class Derived : public Base
{
public:
    void vf(int);    // overrides `Base::vf(int)`
    void vf(double); // overrides `Base::vf(double)`
};
Run Code Online (Sandbox Code Playgroud)


AnT*_*AnT 6

调用虚拟成员函数和调用非虚拟成员函数之间的区别在于,根据定义,在前一种情况下,根据调用中使用的对象表达式的动态类型选择目标函数,而在后者中case使用静态类型.

这里的所有都是它的.你的例子通过p2->doA()p2->doB()调用清楚地说明了这种差异.静态类型的*p2表达式是Parent,而同一表达式的动态类型是Child.这就是p2->doA()电话Parent::doAp2->doB()电话的原因Child::doB.

在这种差异很重要的情况下,名字隐藏根本不会出现在图片中.