2013-04-06 182 views
26

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

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; 
} 
+0

相关:http://stackoverflow.com/q/2437586/951890 – 2013-04-06 16:20:34

+7

如果它不是虚拟的它没有被覆盖,'衍生:: foo'而_hides_'基地:: foo'。 – dyp 2013-04-06 16:21:05

+0

@DyP谢谢,非常高兴知道:) – 2013-04-06 16:38:04

回答

44

您可以随时(*)使用合格-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::foovirtual,然后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

一个合格编号的形式为X :: Y:: Y::之前的部分指定了我们要查找标识符Y的位置。在第一种形式中,我们查询X,然后从X的上下文中查找Y在第二种形式中,我们在全局命名空间中查找Y

未限定ID不包含::,因此没有(自己)指定查找名称的上下文。

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

对于foo,我们现在完成了,并致电Base::foo

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

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

当使用合格代码时,我们明确指出要在哪里查找名称的上下文。表达式Base::foo指出我们要从Base的上下文中查找名称foo(例如,它可以找到Base继承的函数)。 此外,它禁用虚拟分派。

因此,d.Base::foo()会发现Base::foo并称之为; d.Base::bar()会发现Base::bar并称之为。


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

#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&对于this指针(本质上)。这就是为什么第二个人可以从Private_derivedPrivate_derived的朋友那里工作。

+0

你的派生类foo隐藏基类foo。在这种情况下使用限定符语法是否也适用? – nurabha 2016-03-31 20:48:48

+0

@nurabha请你详细说明一下吗?我不太明白你的问题是什么。 – dyp 2016-04-01 12:38:49

+0

实际上,基类foo并未被派生类foo覆盖,但它是隐藏的。如果您将基类指针指向派生对象并使用此指针指向foo进行调用,则将打印base。 base * basePtrToDerived = &bar; basePtrToDerived-> foo(); – nurabha 2016-04-01 14:01:41

9

首先Derived应该从Base继承。

class Derived : public Base{ 

也就是说

首先你可以不必在富衍生

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

class Derived : public Base{ 

} 

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

其次可以使派生:: foo的呼叫基地:: 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; 
} 

三,你可以使用基本的合格ID :: foo的

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

我觉得这个答案比接受的答案更有帮助。 – user23573 2015-09-03 12:04:20

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; 
} 
+0

把它写成'bar.Base :: foo()' – Mankarse 2013-04-06 16:17:11

+1

@Mankarse TIL可能会更好。 – 2013-04-06 16:17:29

+0

如果你让foo()变成虚拟的,仍然需要调用'bar.Base :: foo();'来访问基类的方法吗? – 2013-04-06 16:23:00

0

一个重要[附加]注意:你仍然有编译错误,如果名称隐藏发生。

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

#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; 
} 
相关问题