我用虚函数f()创建了一个类然后在派生类中我重写了它,就像下面的f(int)为什么我不能访问基类函数抛出子实例?
class B{
public:
B(){cout<<"B const, ";}
virtual void vf2(){cout<<"b.Vf2, ";}
};
class C:public B{
public:
C(){cout<<"C const, ";}
void vf2(int){cout<<"c.Vf2, ";}
};
int main()
{
C c;
c.vf2();//error should be vf2(2)
}
答案 0 :(得分:4)
您必须执行using B::vf2
,以便在名称查找期间考虑该功能。否则,只要编译器在遍历来自child的继承树时找到匹配的函数名称 - &gt;父母 - &gt;盛大的父母等等,遍历停止。
class C:public B{
public:
using B::vf2;
C(){cout<<"C const, ";}
void vf2(int){cout<<"c.Vf2, ";}
};
您遇到 name hiding 。这是an explanation of why它发生了吗?
答案 1 :(得分:1)
在C ++中,派生类隐藏了同名的任何基类成员。您仍然可以通过显式限定它来访问基类成员:
int main()
{
C c;
c.B::vf2();
}
答案 2 :(得分:0)
你被名字藏起来了。
名字隐藏在C ++中无处不在:
int a = 0
int main(int argc, char* argv[]) {
std::string a;
for (int i = 0; i != argc; ++i) {
a += argc[i]; // okay, refers to std::string a; not int a;
a += " ";
}
}
它也出现在Base和Derived类中。
面对变化,名字隐藏背后的想法是健壮性。如果这不存在,在这种特殊情况下,那么考虑会发生什么:
class Base {
};
class Derived: public Base {
public:
void foo(int i) {
std::cout << i << "\n";
}
};
int main() {
Derived d;
d.foo(1.0);
}
如果我要向foo
添加Base
重叠更好的匹配(即直接使用double
):
void Base::foo(double i) {
sleep(i);
}
现在,这个程序不会打印1
,而是会睡1秒钟!
这会疯了吗?这意味着无论何时你希望扩展基类,你都需要查看所有派生类,并确保你不会意外地窃取来自它们的一些方法调用!!
为了能够在不破坏派生类的情况下扩展基类,名称隐藏起作用。
using
指令允许您导入派生类中真正需要的方法,其余方法可以安全地忽略。这是一种白名单方法。
答案 3 :(得分:0)
当您使用派生类中的版本重载基类中的成员函数时,将隐藏基类函数。也就是说,您需要显式限定对基类函数的调用,或者您需要使用using声明来使基类函数通过派生类的对象可见:
struct base {
void foo();
void bar();
};
struct derived: base {
void foo(int);
using base::foo;
void bar(int);
};
int main() {
derived().foo(); // OK: using declaration was used
derived().bar(); // ERROR: the base class version is hidden
derived().base::bar(); // OK: ... but can be accessed if explicitly requested
}
这样做的原因是,当派生函数声明成员函数但从基类中选择了一个更好的匹配时,它被认为是混乱和/或危险(显然,这只适用于成员函数相同数量的论点)。当基类过去没有某个成员函数时,还有一个陷阱:你不希望你编程突然调用另一个成员函数只是因为成员函数被添加到基类。
隐藏基类成员函数的主要烦恼是当存在一组公共虚函数时,您只想在派生类中重写其中一个。虽然只是添加覆盖不会使用指针或对基类的引用来更改接口,但派生类可能无法以自然的方式使用。传统的解决方法是将公共的非虚拟重载分派给受保护的虚拟功能。 C ++标准库中各个方面的虚拟成员函数就是这种技术的一个例子。