当前课程知识点:C++语言程序设计进阶 > 第七章 继承与派生 > 继承方式 > 私有继承和保护继承
私有继承和保护继承
继承的访问控制
基类的public和protected成员:都以private身份出现在派生类中;
基类的private成员:不可直接访问。
访问权限
派生类中的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
通过派生类的对象:不能直接访问从基类继承的任何成员。
Point.h
#ifndef _POINT_H #define _POINT_H class Point { //基类Point类的定义 public: //公有函数成员 void initPoint(float x = 0, float y = 0) { this->x = x; this->y = y;} void move(float offX, float offY) { x += offX; y += offY; } float getX() const { return x; } float getY() const { return y; } private: //私有数据成员 float x, y; }; #endif //_POINT_H
Rectangle.h
#ifndef _RECTANGLE_H #define _RECTANGLE_H #include "Point.h" class Rectangle: private Point { //派生类定义部分 public: //新增公有函数成员 void initRectangle(float x, float y, float w, float h) { initPoint(x, y); //调用基类公有成员函数 this->w = w; this->h = h; } void move(float offX, float offY) { Point::move(offX, offY); } float getX() const { return Point::getX(); } float getY() const { return Point::getY(); } float getH() const { return h; } float getW() const { return w; } private: //新增私有数据成员 float w, h; }; #endif //_RECTANGLE_H
main.cpp
#include <iostream> #include <cmath> using namespace std; int main() { Rectangle rect; //定义Rectangle类的对象 rect.initRectangle(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; return 0; }
继承的访问控制
基类的public和protected成员:都以protected身份出现在派生类中;
基类的private成员:不可直接访问。
访问权限
派生类中的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
通过派生类的对象:不能直接访问从基类继承的任何成员。
protected 成员的特点与作用
对建立其所在类对象的模块来说,它与 private 成员的性质相同。
对于其派生类来说,它与 public 成员的性质相同。
既实现了数据隐藏,又方便继承,实现代码重用。
如果派生类有多个基类,也就是多继承时,可以用不同的方式继承每个基类。
class A { protected: int x; }; int main() { A a; a.x = 5;//错误 } class A { protected: int x; }; class B: public A{ public: void function(); }; void B:function() { x = 5; //正确 }
class A { public: void setA(int); void showA() const; private: int a; }; class B { public: void setB(int); void showB() const; private: int b; }; class C : public A, private B { public: void setC(int, int, int); void showC() const; private: int c; };
void A::setA(int x) { a=x; } void B::setB(int x) { b=x; } void C::setC(int x, int y, int z) { //派生类成员直接访问基类的 //公有成员 setA(x); setB(y); c = z; } //其他函数实现略 int main() { C obj; obj.setA(5); obj.showA(); obj.setC(6,7,9); obj.showC(); // obj.setB(6); 错误 // obj.showB(); 错误 return 0; }
大家好
欢迎回来继续学习
C++语言程序设计
私有继承呢
是最严格的一种继承
基类的公有和保护成员
继承过来以后呢
都变成了私有成员了
那么基类中的原来私有成员
当然还是不可以直接访问
那这种继承呢
其实用得不多
用的时候呢
你要明确目标
为什么要私有继承
实际上它起到了一个
将基类的原有对外接口
都给它封闭掉的这样一个作用
那么基类继承过来的成员
在派生类之外
通过派生类的对象
就不能直接访问了
那么基类继承过来的公有
和保护成员呢
在派生类的内部
还可以访问
接下来呢
我给大家演示一个
私有继承的例子
在这个例子中呢
我们还是定义一个点类
然后让矩形类继承点类
只不过呢这一次
我们让矩形类
以私有方式来继承点类
然后还是在主函数中
定义矩形类的对象
通过矩形类的对象呢
去调用它的成员函数
这一次看看
调用的是哪个类定义的成员函数
好 我们通过单步执行跟踪进去
看看现在调用的移动
是哪个函数
我们跟踪进去
我们看是进到了
Rectangle的成员函数中
在这个例题中
我们看到
在Rectangle类里面
自己也定义了一个move函数
为什么呢
因为point类的move函数
被继承到Rectangle类以后
就成了私有的了
因为它是私有继承
私有成员函数呢
在类外通过对象
是不能直接调用的
那么我们去
怎么去移动一个矩形呢
虽然私有的成员函数
在类外不能被调用
但是在类体里面
在类的成员函数里面
我们是可以使用它的
所以呢
在这儿我们通过
基类类名和作用域分辨符
在这里调用从基类继承过来的
这个move函数
也就是点类的move函数
这样就达到了
移动矩形的这样一个目的
同样呢
getX getY函数也是一样的
我们继续运行
接下来我们需要进到
这些get函数里面了
第一次还是进到
自己定义的Rectangle定义的
getH函数里面了
执行完了以后
再执行进入到
Rectangle自己定义的getW
函数里面了
接下来呢
进到了getY函数里面
这一次是Rectangle自己定义的
因为基类继承过来了getY
在类外也不能直接被访问了
但是在本类的成员函数中
它是可以访问的
所以呢
在Rectangle的getY函数中
调用了point类继承过来的
这个getY函数
取得了Y坐标的值 返回
同样
getX函数也是一样
调用了Rectangle类
自己定义的getX函数
那么通过这个例子我们就看到了
以私有方式继承的时候
公有成员
到了派生类中也成了私有的
在类外不能直接访问了
这样派生类呢就要解决
必要的这种访问接口的问题
所以Rectangle类
自己定义了一些新的访问接口
那么在函数体中
调用了基类继承过来的函数
保护继承呢
是介于公有和私有之间的一种
继承方式
从第四章开始
大家就一直存着一个疑问
保护成员到底是什么
那么保护继承的时候
基类的公有成员和保护成员
到了派生类以后
都成保护成员了
这个保护成员它的特点就是
派生类中的成员函数
是可以直接访问它的
那么基类的保护成员
到了派生类以后
还是保护成员
大家看到了
不管公有继承还是保护继承
都是基类的保护成员
到了派生类中
还是保护成员
那么也就是说
这个保护成员
它向下派生的时候
是可以被派生类成员
直接访问的
但是在类外呢
在整个类家族之外呢
它就像私有成员一样
是不能被访问的
这就是保护成员的特点
所以经过保护继承以后
在类外通过派生类的对象
就不能访问那些
从基类继承过来的成员了
只能派生类新增的成员
保护成员的特点呢
我们简单归纳一下
对于建立其所在类
对象的模块来说
它与私有成员的性质相同
对于它的派生类来说
它与公有成员的性质相同
既实现了数据隐藏
又方便继承
实现了代码的重用
下面的例题呢
对比了从不同的位置
访问保护成员时候的情况
我们看这里定义了一个类a
其中有一个保护成员x
那这个x在类外能不能访问呢
我们测试一下
定义一个主函数
在主函数中
定义一个A类的对象a
我们试图用a.x的方式
去访问对象A的保护成员
这个时候编译器会给我们报错
因为保护成员在类外去看它
就像私有的一样
它是不可直接访问的
但是呢
如果我们定义一个B
作为派生类去继承A类
用公有的方式继承A类
那么A的类的保护成员
就被继承到B类里面来了
这个时候
我们在B类的成员函数中
直接访问这个保护成员x
这就是可以的
就是正确的
所以保护成员呢
当它以公有方式和保护方式
继承到派生类中以后
派生类的成员函数
是可以访问它的
但是在类外是不可访问
这就是保护成员它的特点
大家看到了
这样的保护成员语法以后
是不是感觉保护成员挺好
它既然能够对于类家族之外
隐藏细节
又能够让这个成员
能被派生类访问
两全其美
是不是很好呢
其实保护成员呢有它的优势
但是也要慎用
用在什么场合可以呢
如果基类和派生类
都是我们自己写的
我们这一个项目
一个团队自己写的
这没有问题
如果说
基类和派生类
不是同一组人写的
或者你干脆是通过某种途径
去买的类库等等
那么那个时候
你去用它的保护成员的细节
就不那么方便了
这个深入的东西
大家可以自己想一想
是不是这么回事
如果派生类有多个基类
也就是多继承的时候
那么每个基类
我们可以采用不同的继承方式
先来看看这个例题
在这里我们定义了A类和B类
定义了C类呢
继承了A和B
它继承两个类
它是以公有方式继承A
以私有方式继承B
A类和B类各自有自己的数据成员
和相应的公有访问接口
它的公有成员函数
C类也自己新增了数据成员
除了继承以外
然后也定义了自己的成员函数
那么我们来看
C类的成员函数
setC它的实现
这些函数是为了给数据成员
设置值
但是它直接设置
直接赋值
只能给自己类新增的
这个成员c赋值
那么从A类继承来的成员
从B类继承来的成员
都要调用C类的公有成员函数
来进行设置
在主函数中
我们定义了C类的对象 obj
那么这个对象
通过这个对象调用setA
showA没有问题
A类的公有成员
被继承到C类中来
仍然是公有成员
调用C类自己新增的公有成员
setC showC也没有问题
但是如果你试图调用
B类的成员函数
那编译器就会给我们报错
为什么呢
因为B类是私有继承的
B类的函数
公有函数到了C里面
也变成私有的了
在类外就不能直接访问了
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义