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

时间:2013-04-06 16:10:01

标签: 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;
}

4 个答案:

答案 0 :(得分:56)

您可以使用 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不是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
}

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

qualified-id 的格式为X :: Y:: Y::之前的部分指定了我们要查找标识符Y的位置。在第一种形式中,我们会查找X,然后从Y的上下文中查找X 在第二种形式中,我们查找{ {1}}在全局命名空间中。

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

在表达式::中,b->foob都是 unqualified-ids 。在当前上下文中查找foo(在上面的示例中是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的上下文中查找名称foo时,我们会找到Derived。这就是Derived::foo被称为隐藏 Derived::foo的原因。 Base::food.foo()的成员函数内的表达式,仅使用Derivedfoo(),将从this->foo()的上下文中查找。

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

因此,Base会找到d.Base::foo()并将其调用; Base::foo会找到d.Base::bar()并将其调用。


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

Base::bar

请注意, access-specifiers 类成员和基类都会影响您是否可以使用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
}

辅助功能与名称查找正交。因此,名称隐藏不会对其产生影响(您可以在派生类中省略#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_fun,并为qualified-id调用获取相同的行为和错误。)

private_fun中的错误与p.Base::private_fun()中的错误有所不同:第一个错误已经无法引用名称r.Base::public_fun()(因为它是私有名称)。第二个无法将Base::private_funr转换为Private_derived&以获取Base& - 指针(基本上)。这就是为什么第二个适用于thisPrivate_derived的朋友。

答案 1 :(得分:10)

首先,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;
 }

答案 2 :(得分: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;
}

答案 3 :(得分:0)

重要的[附加]注释:如果 名称隐藏 ,则仍会出现编译错误。

在这种情况下,要么使用using关键字,要么使用qualifer。另外,请参阅this answer

System.Text.RegularExpressions.Regex.Replace("This is a stack of pages on stack overflow. Sometimes also referred to as stack.overflow.com"
    ,"stack(?!\.overflow)"
    ,"STACK")