您可以随时(*)使用合格-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
)
一个合格编号的形式为X :: Y
或:: Y
。 ::
之前的部分指定了我们要查找标识符Y
的位置。在第一种形式中,我们查询X
,然后从X
的上下文中查找Y
。在第二种形式中,我们在全局命名空间中查找Y
。
未限定ID不包含::
,因此没有(自己)指定查找名称的上下文。
在表达b->foo
,既b
和foo
是不合格的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_fun
和private_fun
,并为合格ID调用获取相同的行为和错误)。
错误p.Base::private_fun()
与r.Base::public_fun()
中的错误的方式不同:第一个错误已经无法引用名称Base::private_fun
(因为它是一个私人名称)。第二个不能将r
从Private_derived&
转换为Base&
对于this
指针(本质上)。这就是为什么第二个人可以从Private_derived
或Private_derived
的朋友那里工作。
相关:http://stackoverflow.com/q/2437586/951890 – 2013-04-06 16:20:34
如果它不是虚拟的它没有被覆盖,'衍生:: foo'而_hides_'基地:: foo'。 – dyp 2013-04-06 16:21:05
@DyP谢谢,非常高兴知道:) – 2013-04-06 16:38:04