继承中类的作用域

派生类的作用域嵌套在其基类的作用域之内,如果一个名字无法在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义。

名字冲突与继承

派生类中能重定义在其直接基类或间接基类中的名字,此时定义在内层作用域(即派生类)的名字将隐藏定义在外层作用域(即基类)的名字。

struct Base{
	Base():mem(0){}
protected int mem;
};

struct Derived : Base{
	Derived(int i):mem(i){}
	int get_mem() {return mem;}
protected int mem;		//@ 隐藏基类中的mem
};

通过作用域符来使用隐藏的成员

可以通过作用域符来访问被隐藏的基类成员:

struct Derived : Base{
	int get_base_mem(){return Base::mem;}
	//...
};

作用运算符将覆盖掉原有的查找规则,并指示编译器从 Base 类的作用域开始查找 mem

注意:

除了覆盖继承而来的虚函数之外,派生类最好不要重用其他定义在基类中的名字。

一如往常,名字查找先于类型检查

如果派生类的成员与基类的某个成员同名,则派生类将在其作用域内隐藏该基类成员,即使派生类成员与基类成员的形参列表不一致,基类成员也会被隐藏。

struct Base{
	int memfunc();
};

struct Derived : Base{
	int memfunc(int);
};

Derived d;Base b;
b.memfunc();		//@ 正确
d.memfunc(10);		//@ 正确
d.memfunc();		//@ 错误,参数列表为空的基类成员函数被隐藏
d.Base::memfunc();		//@ 正确

虚函数与作用域

基类和派生类的虚函数接受的实参必须相同,否则就无法通过基类的引用或者指针调用派生类的虚函数。

class Base{
public:
	virtual int fun();
};

class D1 : public Base{
public:
	int fun(int);		//@ 隐藏了Base中的fun函数
	virtual void f2();
};

class D2 : public D1{
public:
	int fun(int);		//@ 隐藏了D1 中的 fun 函数
	int fun();			//@ 覆盖了Base 中的 fun 函数
	void f2();			//@ 覆盖了 D1 中的 f2 函数
};

通过基类调用隐藏的虚函数

Base bobj;D1 d1obj;D2 d2obj;
Base* bp1 = &bobj,*bp2 = &d1obj,*bp3 = &d2obj;

bp1->fun();		//@ 调用Base::fun
bp2->fun();		//@ 调用Base::fun
bp3->fun();		//@ 调用D2::fun

D1 *d1p = &d1obj;D2 *d2p = &d2obj;
bp2->f2();		//@ 错误,Base没有名字为f2的成员
d1p->f2();		//@ 调用D1::f2
d2p->f2();		//@ 调用D2::f2

Base *p1 = &d2obj;D1 *p2 = &d2onj;D2 *p3 = &2dobj;
p1->fun(42);		//@ 错误,Base中没有接受一个int的fun
p2->fun(42);		//@ 静态绑定,调用D1::fun(int)
p3->fun(42);		//@ 静态绑定,调用D2::fun(int)

覆盖重载的函数

成员函数无论是否是虚函数,都可以被重载,派生类可以覆盖重载函数的0个或多个实例,如果派生类希望所有的重载版本对它来说都是可见的,那么它就需要覆盖所有的版本,或者一个也不覆盖。

有时候只需要覆盖重载集合中的一个版本,而不得不覆盖基类中的每一个版本,显然很麻烦。一种的好的解决办法是为重载成员提供一条 using 声明语句,这样就不需要覆盖基类中的每一个重载版本。using 声明语句指定了一个名字而不指定形参列表,所以一条基类成员函数的 using 声明语句就能把该函数的所有重载实例添加到派生类的作用域中,此时派生类只需要定义其特有的版本即可,无需为继承而来的。

原文地址:https://www.cnblogs.com/xiaojianliu/p/12496349.html