如何在派生类对象中调用被覆盖的基类方法?
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;
}
如何在派生类对象中调用被覆盖的基类方法?
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;
}
#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;
}
[也修正了一些原始问题的拼写错误。]
(*) 访问限制仍然适用,并且基类可能会有歧义。
如果 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
的签名与Base::bar
兼容时,即使不使用virtual
关键字,Derived::bar
也会隐式地成为虚函数。)
限定符标识符(qualified-id)可以采用X :: Y
或:: Y
的形式。冒号前面的部分指明我们要在哪里查找标识符Y
。在第一种形式中,我们首先查找X
,然后从X
的上下文中查找Y
。在第二种形式中,我们在全局命名空间中查找Y
。
非限定符标识符(unqualified-id)不包含::
,因此不指定查找名称的上下文。b->foo
中,b
和foo
都是非限定符标识符。b
在当前上下文中查找(在本例中是main
函数)。因为b->foo
具有类成员访问的形式,所以我们从b
的类型(或者说*b
的类型)的上下文中查找foo
。因此,我们从Base
的上下文中查找foo
。我们将找到在Base
内部声明的成员函数void foo()
,我将其称为Base::foo
。foo
,我们现在完成了,并调用Base::foo
。b->bar
,我们首先找到Base::bar
,但它被声明为虚函数。因为它是虚函数,我们执行虚分派。这将调用指向对象b
类型的类层次结构中的最终覆盖函数。因为b
指向一个类型为Derived
的对象,所以最终的覆盖函数是Derived::bar
。Derived
的上下文中查找名称foo
时,我们将找到Derived::foo
。这就是为什么说Derived::foo
会隐藏Base::foo
的原因。诸如d.foo()
或在Derived
的成员函数内部使用简单的foo()
或this->foo()
的表达式将从Derived
的上下文中查找。使用限定符id时,我们明确声明要查找名称的上下文。表达式Base::foo
表示我们想要从Base
的上下文中查找名称foo
(例如,它可以找到Base
继承的函数)。 此外,它禁用了虚拟派发。
因此,d.Base::foo()
将找到Base::foo
并调用它; d.Base::bar()
将找到Base::bar
并调用它。
有趣的事实:纯虚函数可以有实现。它们不能通过虚拟派发进行调用,因为它们需要被覆盖。但是,您仍然可以使用限定符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
和private_fun
,并获得限定标识符调用的相同行为和错误)。
顺便说一下,在p.Base::private_fun()
中的错误与r.Base::public_fun()
中的错误不同: 第一个错误已经无法引用名称Base::private_fun
(因为它是一个私有名称)。第二个错误无法将r
从Private_derived&
转换为Base&
以用作this
指针(基本上是这样)。这就是为什么第二个错误在Private_derived
内部或Private_derived
的友元中有效。
Base::foo
的方式。使用限定符标识符调用基类函数与该函数在派生类中发生的任何变化无关 - 它可以被隐藏,可以被重写,可以通过使用 using 声明变为私有,使用限定符标识符时直接访问基类函数。 - dyp首先,Derived 应该继承自 Base。
class Derived : public Base{
话虽如此
首先,你可以在派生类中不使用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;
}
考虑一开始就将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;
}
bar.Base::foo()
。 - Mankarsebar.Base::foo();
来访问基类方法,对吗? - Xun Yangvoid Base1::begin(){\\initialization}
, class Derived:public Base1, public Base2{...}; void Derived::begin(){\\initialization}
,所以架构很遗憾超出了我的能力范围 :( 只是在这里尝试进行一些 hackish 测试 :) - Xun Yang一个重要的[附加]说明:如果发生名称隐藏,您仍然会遇到编译错误。
在这种情况下,可以使用using关键字或使用限定符。此外,还可以参考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;
}
Derived
有它自己的foo
版本,那么很有可能它并没有被设计成在类外部调用基础版本时能正确地运行。 - Pete Becker