5、继承与派生2-访问控制
基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象,就只能访问该基类的公有成员。
由于继承所导致的原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同,这里说的访问来自两个方面:一是派生类中的新增成员访问从基类继承的成员;二是在派生类外部(非类族内的成员),通过派生类的对象访问从基类继承的成员。
1、公有继承
当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。---也就是说基类的公有成员和保护成员被继承到派生类中访问属性不变,仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问他们。在类族之外只能通过派生类的对象访问从基类继承的公有成员,而无论是派生类的成员还是派生类的对象都无法直接访问基类的私有成员。
eg:
point类公有继承。
在这个例子中从point类派生出新的Rectangle(矩形)类。矩形是由一个点加上长、宽构成,矩形的点具备了point类的全部特征,同时矩形自身也有一些特点,这就需要在继承point类的同时添加新的成员。
程序的头文件部分
//Rectangle.h
class Point //基类Point类的定义
{
public:
void InitP(float xx=0,float yy=0){X=xx;Y=yy;}
void Move(float xOff,float yOff){X+=xOff;Y+=yOff;}
float GetX(){return X;}
float GetY(){return Y;}
private:
float X,Y;
};
class Rectangle:public Point //派生类定义部分
{
public: //新增公有函数成员
void InitR(float x,float y,float w,float h)
{InitP(x,y);W=w;H=h;} //调用基类公有成员函数
float GetH(){return H;}
float GetW(){return W;}
private: //新增私有数据成员
float W,H;
};
//End of Rectangle.h
首先定义了基类point,派生类Rectangle继承了point类的全部成员
因为继承方式为公有形式,故基类中的公有成员在派生类中访问属性保持原样,派生类的成员函数及对象可以访问到基类的公有成员。基类原有的外部接口变成了派生类的外部接口的一部分。当然派生类自己新增的成员之间都是可以相互访问的。
下面是程序的主函数部分
#include<iostream>
#include<cmath>
#include"rectangle.h"
using namespace std;
int main()
{
Rectangle rect;//声明Rectangle类的对象
rect.InitR(2,3,20,10);//设置矩形的数据
rect.Move(3,2);//移动矩形位置
cout<<"The data of rect(X,Y,W,H):"<<endl;
cout<<rect.GetX()<<","
<<rect.GetY()<<","
<<rect.GetW()<<","
<<rect.GetH()<<endl;
}
2、私有继承
当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。也就是说基类的公有和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问他们,但是在类族外部通过派生类的对象无法直接访问他们。无论是派生类的成员还是通过派生类的对象,都无法直接访问从基类继承的私有成员。
经过私有继承之后,所有基类的成员都成为了派生类的私有成员或不可直接访问的成员,如果进一步派生的话,基类的全部成员就无法在新的派生类中被直接访问。因此,私有继承之后,基类的成员再也无法在以后的派生类中直接发挥作用,实际是相当于中止了基类功能的继续派生。
eg:point类私有继承
class Point //基类Point类的定义
{
public:
void InitP(float xx=0,float yy=0){X=xx;Y=yy;}
void Move(float xOff,float yOff){X+=xOff;Y+=yOff;}
float GetX(){return X;}
float GetY(){return Y;}
private:
float X,Y;
};
class Rectangle:private Point //派生类定义部分
{
public: //新增公有函数成员
void InitR(float x,float y,float w,float h)
{InitP(x,y);W=w;H=h;} //调用基类公有成员函数
float GetX(){return Point::GetX();}
float GetY(){return Point::GetY();}
float GetH(){return H;}
float GetW(){return W;}
private: //新增私有数据成员
float W,H;
};
在私有继承情况下,为了保证基类的一部分外部接口特征能够在派生类中也存在,就必须在派生类中重新声明同名的成员。比如这里在派生类中重新声明了Move,GetX,GetY等函数,利用派生类对基类成员的访问能力,把基类的原有成员函数的功能照搬过来。
程序主函数部分
#include<iostream>
#include<cmath>
#include"rectangle.h"
using namespace std;
int main()
{
Rectangle rect;//声明Rectangle类的对象
rect.InitR(2,3,20,10);//设置矩形的数据
rect.Move(3,2);//移动矩形位置
cout<<"The data of rect(X,Y,W,H):"<<endl;
cout<<rect.GetX()<<","
<<rect.GetY()<<","
<<rect.GetW()<<","
<<rect.GetH()<<endl;
getchar();
}
注意:
1、::是C++里的“作用域运算符”。作用域符号::的前面一般是类名称,后面一般是该类的成员名称。
2、其作用主要有三点:
(1)标识作用域的级别;
:: 用在全局函数或变量前,表示是全局函数或变量
(2)标识成员属于哪个类
A,B表示两个类,在A,B中都有成员member。那么A::member就表示类A中的成员member
B::member就表示类B中的成员member
(3) 限定成员的作用范围
应用于命名空间namespace定义的实体后面。
3、保护继承
保护继承中,基类的公有和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。这样,派生类的其他成员就可以直接访问从基类继承来的公有和保护成员,但在类外部通过派生类的对象无法直接访问他们。
如果保护继承的派生类作为新的基类,继续派生时,派生出来的类有可能可以访问间接从基类中继承来的成员。
如果合理地利用保护成员,就可以在类的复杂层次关系中在共享与成员隐藏之间找到一个平衡点,即能实现成员隐藏,又能方便继承,实现代码的高效重用和扩充。
eg:假设类A有保护数据成员x,我们来讨论成员x的访问特征。
基类A的定义为:
class A
{
protected:
int x;//保护数据成员
};
主函数:
int main()
{
A a;
a.x=5;//错误
}
在编译时会错误,因为在建立A类对象的模块中试图访问A类的保护成员,这是不允许的。
如果A类以公有方式派生了B类,则在B类中,A类保护成员和该类的公有成员一样可以访问的,eg
class A
{
protected:
int x;
};
class B:public A //公有派生
{
public :
void function();
};
void B::function()
{x=5;}这是在类外面来定义函数的方式,也可以直接在声明的同时直接定义。
在派生类B的成员函数function内部,是完全可以访问基类的保护成员的。