继承与派生——访问控制
一.知识要点
(一)知识回顾:
基类的成员可以有public、protected、private三种访问属性。基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象,就只能访问该类的公有成员。
(二)知识引入:
类的继承方式有public(公有继承)、protected(保护继承)、private(私有继承)三种。不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这里的访问分别指两方面:
- 派生类的新增成员访问从基类继承的成员
- 派生类外部(非类族内的成员),通过派生类的对象访问从基类继承的成员
下面我们将分别介绍public(公有继承)、protected(保护继承)、private(私有继承)三种。
二.公有继承
(一)知识要点:
1.当类的继承方式为公有继承时,基类的公有成员和保护成员仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问他们。
2.在类之外只能通过派生类的对象访问从基类继承的公有成员。
(二)代码分析
(教材256页)
#include
using namespace std;
class Point{
public:
void initPoint(float x=0,float y=0)
{
this->x=x;
this->y=y;
}//构造函数
void move(float movex,float movey)
{
x=x+movex;
y=y+movey;
}
float getx() const {return x;}
float gety() const {return y;}
private:
float x,y;
};
class Rectangle:public Point//派生类的定义
{
public:
void initRectangle(float x,float y,float w,float h)
{
this->w=w;
this->h=h;
initPoint(x,y);//直接使用基类的公有成员函数
}
float geth() const {return h;}
float getw() const {return w;}
private:
float w,h;
};
int main()
{
Rectangle rect;//定义Rectangle类的对象
rect.initPoint(6,6);
cout<
运行结果:
1.修改代码:这里我们让initpoint函数的属性是protected,同时在主函数中不让对象rect调用。
运行结果:
编译时没有报错,说明在公有继承下,派生类Rectangle可以直接访问保护成员。
2.修改代码:这里我们让initpoint函数的属性是protected,同时不改变原始的主函数,即让对象rect调用基类里的两个函数。
运行结果:
编译时发生了错误,错误说initpoint的属性是protected,而对象rect在调用属性为public的move函数时,并没有发生错误说明派生类的对象无法访问基类中的保护成员和私有成员,只可以访问基类中的公有成员。
三.私有继承
(一)知识要点:
1.当类的继承方式为私有继承的时候,基类中的公有成员和保护成员都以私有成员的身份出现在派生类中,派生类的其他成员可以直接访问基类中的公有成员和保护成员。
2.在类族外部通过派生类的对象无法直接基类的公有成员,保护成员和私有成员
(二)代码分析
(以下代码非教材上的代码,教材上私有继承的代码请参考256页)
#include
using namespace std;
class point
{
private:
int a;
public:
void inita(int x){a=x;}
int geta(){return a;}
};
class p:private point
{
private:
int b;//派生类的私有数据成员
public:
void init(int x,int y)
{
b=y;
inita(x);//调用基类的成员函数
}
int getb(){return b*geta();}//调用基类的成员函数
};
int main()
{
p op;//创建派生类对象
op.init(2,3);
cout<
运行结果
1.修改代码:
我们在类外通过对象来访问从基类私有继承来的成员inita()
运行结果:
编译发生了错误,证明了类外通过对象是无法访问从基类私有继承来的成员的。那我们该如何修改它呢?
2.修改代码:
我们继续上面的问题,把代码进行如下的修改,我们在派生类中重新声明了基类中的函数,利用派生类对基类成员的访问能力,把基类的原有成员函数的功能照搬过来即可。
运行结果:
我们可以看到进行了第二次修改以后,代码可以成功编译了,我们也因此可以解决了类外通过对象来访问基类私有继承来的成员函数的问题了。
3.修改代码:
最后我们进一步完善整个代码成如下的样子
#include"pch.h"
#include
using namespace std;
class point
{
private:
int a;
public:
void inita(int x) { a = x; }
int geta() { return a; }
};
class p :private point
{
private:
int b;//派生类的私有数据成员
public:
void init(int x, int y)
{
b = y;
inita(x);//调用基类的成员函数
}
int getb() { return b * geta(); }
void inita(int x) { point::inita(x); }//修改地方2处
int geta(){ return point::geta();}//调用基类的成员函数
};
int main()
{
p op;//创建派生类对象
op.init(2, 3);
op.inita(12);//修改地方1处
cout<
运行结果:
四.保护继承
(一)知识要点:
1.当类的继承方式为保护继承的时候,基类中的公有成员和保护成员都以宝华成员的身份出现在派生类中,派生类的其他成员可以直接访问基类中的公有成员和保护成员。
2.在类的外部通过派生类的对象无法直接访问它们。
(二)代码分析
#include"pch.h"
#include
using namespace std;
class point
{
private:
int a;
public:
void inita(int x) { a = x; }
int geta() { return a; }
};
class p :protected point//保护继承
{
private:
int b;//派生类的私有数据成员
public:
void init(int x, int y)
{
b = y;
inita(x);//调用基类的成员函数
}
int getb() { return b * geta(); }
void inita(int x) { point::inita(x); }
int geta(){ return point::geta();}//调用基类的成员函数
};
int main()
{
p op;//创建派生类对象
op.init(3, 4);
cout<
运行结果:
代码在私有继承的基础上进行了一点修改,在两方面上的访问属性上,保护继承和私有继承一样,那他们到底有什么区别呢?我们将在文章结尾进行讨论。
五.知识点汇总
- 基类成员对派生类的可见性
public | protected | private | |
---|---|---|---|
公有继承 | 可见 | 可见 | 不可见 |
私有继承 | 可见 | 可见 | 不可见 |
保护继承 | 可见 | 可见 | 不可见 |
- 基类成员对派生类对象的可见性
public | protected | private | |
---|---|---|---|
公有继承 | 可见 | 不可见 | 不可见 |
私有继承 | 不可见 | 不可见 | 不可见 |
保护继承 | 不可见 | 不可见 | 不可见 |
问题思考: 在私有继承和保护继承下,基类的所有成员在派生类中或派生类对象中的访问属性都是相同的,那么两者到底有什么区别? |
其实当进行两次派生的时候,他们的区别就出现了。我们假设派生方式是 A->B->C
(a)当B私有继承A的时候,在B类中A类的所有成员都将是私有的了,因此无论C以后再以什么方式来继承B中的成员,在C类中都将无法访问到A类中的成员了。
(b)当B保护继承A时,在B类中A类的公有成员和保护成员都将是保护成员了,这样无论C是以什么方式来继承B中的成员,在C类中都可以访问A中的公有成员和保护成员。
通过上面的讨论,我们发现私有继承之后,基类的成员再也无法在以后的派生类中直接发挥作用,实际上是相当于中止了基类功能的继续派生。