从派生类对象调用基类方法

Xun*_*ang 36 c++ inheritance

如何从派生类对象中调用派生类重写的基类方法?

class Base{
  public:
    void foo(){cout<<"base";}
};

class Derived:public Base{
  public:
    void foo(){cout<<"derived";}
}

int main(){
  Derived bar;
  //call Base::foo() from bar here?
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

dyp*_*dyp 58

您可以使用qualified-id始终(*)引用基类的函数:

#include <iostream>

class Base{
  public:
    void foo(){std::cout<<"base";}
};

class Derived : public Base
{
  public:
    void foo(){std::cout<<"derived";}
};

int main()
{
  Derived bar;
  //call Base::foo() from bar here?
  bar.Base::foo(); // using a qualified-id
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

[还修正了OP的一些拼写错误.]

(*)访问限制仍然适用,基类可能不明确.


如果Base::foo不是virtual,则Derived::foo不会覆盖 Base::foo.相反,Derived::foo 隐藏 Base::foo.可以在以下示例中看到差异:

struct Base {
   void foo()         { std::cout << "Base::foo\n"; }
   virtual void bar() { std::cout << "Base::bar\n"; }
};

struct Derived : Base {
   void foo()         { std::cout << "Derived::foo\n"; }
   virtual void bar() { std::cout << "Derived::bar\n"; }
};

int main() {
    Derived d;
    Base* b = &d;
    b->foo(); // calls Base::foo
    b->bar(); // calls Derived::bar
}
Run Code Online (Sandbox Code Playgroud)

(Derived::bar即使您不使用virtual关键字,也是隐式虚拟的,只要它的签名兼容Base::bar.)

一个合格的-ID或者是形式的X :: Y,或只是:: Y.前面的部分::指定了我们要查找标识符的位置Y.在第一种形式中,我们查找X,然后Y 从内部X查找.在第二种形式中,我们查找Y全局命名空间.

一个不合格的-ID不包含::,因此不(本身)指定一个范围内去哪里找了名.

在表达式中b->foo,两个bfoo不合格的IDS.b在当前上下文中查找(在上面的示例中是main函数).我们找到了局部变量Base* b.因为b->foo具有类成员访问的形式,我们foo从类型b(或更确切地说*b)的类型的上下文中查找.所以我们foo从上下文中查找Base.我们将在void foo()内部声明成员函数Base,我将其称为Base::foo.

因为foo,我们现在已经完成了,并致电Base::foo.

因为b->bar,我们首先发现Base::bar,但它被宣布virtual.因为它是virtual,我们执行虚拟调度.这将调用对象类型的类层次结构中的最终函数覆盖b.因为b指向类型的对象Derived,所以最终的覆盖是Derived::bar.

fooDerived上下文中查找名称时,我们会发现Derived::foo.这就是为什么Derived::foo据说隐藏 Base::foo.表达式,例如,d.foo()或在成员函数内Derived,使用简单foo()this->foo(),将从上下文中查找Derived.

使用qualified-id时,我们明确说明了查找名称的位置的上下文.表达式Base::foo声明我们想要foo从上下文中Base查找名称(Base例如,它可以找到继承的函数).此外,它禁用虚拟调度.

因此,d.Base::foo()会找到Base::foo并称之为; d.Base::bar()会找到Base::bar并称之为.


有趣的事实:纯虚函数可以有一个实现.它们不能通过虚拟调度来调用,因为它们需要被覆盖.但是,您仍然可以使用qualified-id调用它们的实现(如果有的话).

#include <iostream>

struct Base {
    virtual void foo() = 0;
};

void Base::foo() { std::cout << "look ma, I'm pure virtual!\n"; }

struct Derived : Base {
    virtual void foo() { std::cout << "Derived::foo\n"; }
};

int main() {
    Derived d;
    d.foo();       // calls Derived::foo
    d.Base::foo(); // calls Base::foo
}
Run Code Online (Sandbox Code Playgroud)

请注意,类成员和基类的访问说明符都会影响您是否可以使用qualified-id 在派生类型的对象上调用基类的函数.

例如:

#include <iostream>

struct Base {
public:
    void public_fun() { std::cout << "Base::public_fun\n"; }
private:
    void private_fun() { std::cout << "Base::private_fun\n"; }
};

struct Public_derived : public Base {
public:
    void public_fun() { std::cout << "Public_derived::public_fun\n"; }
    void private_fun() { std::cout << "Public_derived::private_fun\n"; }
};

struct Private_derived : private Base {
public:
    void public_fun() { std::cout << "Private_derived::public_fun\n"; }
    void private_fun() { std::cout << "Private_derived::private_fun\n"; }
};

int main() {
    Public_derived p;
    p.public_fun();        // allowed, calls Public_derived::public_fun
    p.private_fun();       // allowed, calls Public_derived::public_fun
    p.Base::public_fun();  // allowed, calls Base::public_fun
    p.Base::private_fun(); // NOT allowed, tries to name Base::public_fun

    Private_derived r;
    r.Base::public_fun();  // NOT allowed, tries to call Base::public_fun
    r.Base::private_fun(); // NOT allowed, tries to name Base::private_fun
}
Run Code Online (Sandbox Code Playgroud)

可访问性与名称查找正交.因此,名称隐藏对它没有影响(您可以省略public_funprivate_fun在派生类中,并为qualified-id调用获得相同的行为和错误).

中的错误p.Base::private_fun()是从错误中不同r.Base::public_fun()的方式:第一种已经没有提到名字Base::private_fun(因为它是一个专用名称).第二个未能转换rPrivate_derived&Base&this终场前(基本).这就是为什么第二个在内部Private_derived或朋友的作用Private_derived.


小智 12

首先,Derived应该继承自Base.

 class Derived : public Base{
Run Code Online (Sandbox Code Playgroud)

那就是说

首先你可以在Derived中没有foo

class Base{
  public:
    void foo(){cout<<"base";}
};

class Derived : public Base{

}

int main(){
  Derived bar;
  bar.foo() // calls Base::foo()
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

其次,你可以使Derived :: foo调用Base :: foo.

class Base{
  public:
    void foo(){cout<<"base";}
};

class Derived : public Base{
  public:
    void foo(){ Base::foo(); }
                ^^^^^^^^^^
}

int main(){
  Derived bar;
  bar.foo() // calls Base::foo()
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

第三,你可以使用Base :: foo的限定id

 int main(){
    Derived bar;
    bar.Base::foo(); // calls Base::foo()
    return 0;
 }
Run Code Online (Sandbox Code Playgroud)

  • 我觉得这个答案比接受的答案更有帮助. (3认同)