Effective C++——Item33: 避免隐藏继承的名字
一、从原理理解隐藏
- 从变量作用域看隐藏
全局变量x和局部变量x的类型是不同的,但C++的隐藏规则:只隐藏名字(hiding names)。
int x; // global variable
void someFunc()
{
double x; // local variable
std::cin >> x; // read a new value for local x
}
-
对于继承而言,隐藏的原理即为:派生类作用域内嵌在基类作用域中。
-
这是为了强调我们正在谈论名称。该示例还可以包含类型名称,例如枚举、嵌套类和 typedef。在这次讨论中唯一重要的是它们的名字。
-
从例子理解名字(names)的查找过程
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf2();
void mf3();
};
class Derived: public Base {
public:
virtual void mf1();
void mf4() {
//..
mf2();
//..
}
};
在mf4()中查找mf2()的过程:
- 局部作用域mf4()函数
- 包含作用域Derived类
- 下一个包含作用域Base类
- Base类的namespace中查找
二、从实例理解隐藏
- 基类函数被隐藏,即使参数不同
#include <iostream>
using namespace std;
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int) {
cout << "Base::mf1(int)" << endl;
}
virtual void mf2() {
cout << "Base::mf2()" << endl;
}
void mf3() {
cout << "Base::mf3()" << endl;
}
void mf3(double) {
cout << "Base::mf3(double)" << endl;
}
};
class Derived: public Base {
public:
virtual void mf1() {
cout << "Derived::mf1()" << endl;
}
void mf3() {
cout << "Derived::mf3()" << endl;
}
void mf4() {
cout << "Derived::mf4()" << endl;
}
};
int main() {
Derived d;
d.mf1(); // call Derived::mf1
d.mf1(1); // error, Derived::mf1 hides Base::mf1, can't find Base::mf1(int)
d.mf2(); // ok, call Base::mf2
d.mf3(); //ok, hides Base::mf3, call Derived::mf3(),
d.mf3(1.0); // error, Derived::mf3() hides Base::mf3, can't find Base::mf3(double)
return 0;
}
- 如何使被隐藏的函数可见
-
using声明——使得所有某个名字的事物都可见
using Base::mf1;
using Base::mf3;
使得Base类中所有名为mf1、mf3的事物都在Derived类中可见。
#include <iostream>
using namespace std;
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int) {
cout << "Base::mf1(int)" << endl;
}
virtual void mf2() {
cout << "Base::mf2()" << endl;
}
void mf3() {
cout << "Base::mf3()" << endl;
}
void mf3(double) {
cout << "Base::mf3(double)" << endl;
}
};
class Derived: public Base {
public:
using Base::mf1;
using Base::mf3;
virtual void mf1() {
cout << "Derived::mf1()" << endl;
}
void mf3() {
cout << "Derived::mf3()" << endl;
}
void mf4() {
cout << "Derived::mf4()" << endl;
}
};
int main() {
Derived d;
d.mf1(); // ok
d.mf1(1); // ok, call Base::mf1(int)
d.mf2(); // ok, call Base::mf2
d.mf3(); //ok, call Derived::mf3(),
d.mf3(1.0); // ok, Base::mf3(double)
return 0;
}
- 内联转发函数——不希望全部可见
class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
};
class Derived: private Base {
public:
virtual void mf1() // forwarding function; implicitly inline
{
Base::mf1();
}
};
...
Derived d;
int x;
d.mf1(); // fine, calls Derived::mf1
d.mf1(x); // error! Base::mf1() is hidde
三、重载、重写、隐藏的区别
- 函数重载发生在相同作用域
重载时机:参数数量、类型、返回值、是否const函数
- 函数隐藏发生在不同作用域
隐藏时机:隐藏上一级同名变量、函数(不管参数类型、返回类型)
- 函数覆盖就是函数重写。准确地叫做虚函数覆盖和虚函数重写,也是函数隐藏的特例。