46

如何从派生类对象调用被派生类覆盖的基类方法?

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;
}
4

4 回答 4

75

您可以始终(*) 使用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;
}

[还修正了OP的一些错别字。]

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


如果Base::foo不是virtualDerived::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
}

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

一个合格的 id要么是形式X :: Y,要么只是:: Y. 前面的部分::指定我们要在哪里查找标识符Y。在第一种形式中,我们查找X,然后Y X的上下文中查找。Y在第二种形式中,我们在全局命名空间中查找。

unqualified -id不包含 a ::,因此(本身)不指定查找名称的上下文。

在一个表达式b->foo中,bfoo都是不合格的 idsb在当前上下文中查找(在上面的示例中是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()or 之类的表达式,在 的成员函数中Derived,简单地使用foo()or 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
}

请注意,类成员和基类的访问说明符都会影响您是否可以使用限定 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
}

可访问性与名称查找正交。因此,名称隐藏不会对其产生影响(您可以在派生类中省略public_funprivate_fun在限定 ID 调用中获得相同的行为和错误)。

顺便说一下,错误与错误p.Base::private_fun()不同r.Base::public_fun():第一个错误已经无法引用名称Base::private_fun(因为它是私有名称)。第二个无法转换rPrivate_derived&-指针(基本上)。这就是为什么第二个来自内部或朋友的工作。Base&thisPrivate_derivedPrivate_derived

于 2013-04-06T16:13:55.653 回答
18

首先 Derived 应该从 Base 继承。

 class Derived : public Base{

那说

首先,你不能在 Derived 中有 foo

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

class Derived : public Base{

}

int main(){
  Derived bar;
  bar.foo() // calls Base::foo()
  return 0;
}

其次,您可以让 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;
}

第三,您可以使用 Base::foo 的合格 id

 int main(){
    Derived bar;
    bar.Base::foo(); // calls Base::foo()
    return 0;
 }
于 2013-04-06T16:11:13.437 回答
3

首先考虑foo()虚拟化。

class Base {
public:
    virtual ~Base() = default;

    virtual void foo() { … }
};

class Derived : public Base {
public:
    virtual void foo() override { … }
};

但是,这可以完成工作:

int main() {
    Derived bar;
    bar.Base::foo();
    return 0;
}
于 2013-04-06T16:12:08.107 回答
0

一个重要的 [附加] 注意:如果发生名称隐藏,您仍然会遇到编译错误。

在这种情况下,要么使用 using 关键字,要么使用限定符。此外,请参阅此答案

#include <iostream>

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

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

int main()
{
  Derived bar;
  //bar.foo(true,true);      // error:    derived func attempted
  bar.foo(true);             // no error: derived func
  bar.Base::foo(true,true);  // no error: base func, qualified
  return 0;
}
于 2015-04-30T05:39:30.167 回答