继承的学习总结与感悟
继承
继承(Inheritance)是面向对象程序设计(Object Oriented Programming-OOP)中软件重用的关键技术。继承机制使用已经定义的类作为基础建立新的类定义,新的类是原有类的数据及操作与新类所增加的数据及操作组合。新的类把原有的类作为基类引用,而不需要修改原有类的定义。新定义的类作为派生类引用。这种方式减少代码数量,让代码简洁,易懂。
继承:在已有类的基础上创建新类的过程
一个 B 类继承A类,或称从类 A 派生类 B
类 A 称为基类(父类),类 B 称为派生类(子类)
类继承关系的语法形式
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1, 访问控制 基类名2,… , 访问控制 基类名n
访问控制 表示派生类对基类的继承方式,使用关键字:
public 公有继承 //最常用的
继承基类中的保护成员和公有成员,成为派生类中的保护成员和公有成员。
private 私有继承 //基本不用
继承基类中的保护成员和公有成员,成为派生类中的私有成员
protected 保护继承
★不论种方式继承基类,派生类都不能直接使用基类的私有成员
★省略访问控制符时,默认私有继承
派生类的生成过程经历了三个步骤:
●吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
在C++的继承机制中,派生类吸收基类中除构造函数和析构函数之外的全部成员。但是私有成员不能继承。
●改造基类成员
通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏,这一实现需要函数的返回值 ,参数,名称全部相同,否则相当于重载而不是屏蔽)在派生类中不起作用的部分基类成员。
●添加派生类新成员
仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。
派生类拥有基类中除去去私有限定之外的所有数据成员与成员函数。
重名成员的处理
派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员,但是在派生类中用类名限定符使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
派生类中静态成员的访问
基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)
根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质
派生类中访问静态成员,用以下形式显式说明:
类名 :: 成员
或通过对象访问 对象名 . 成员
初始化的处理
在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据
派生类构造函数声明为
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 ) …对象成员n ( 变元表 ) ;
构造函数执行顺序:基类->对象成员->派生类。
注意,积累中没有无参构造函数时,派生中必须写有参构造函数,完成基类的构造。
派生类构造函数和析构函数的使用原则
l 基类的构造函数和析构函数不能被继承
l 如果基类没有定义构造函数或有无参的构造函数,派生类也可以不用定义构造函数
l 如果基类无无参的构造函数,派生类必须定义构造函数
l 如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
l 派生类是否定义析构函数与所属的基类无关
顺序总结
(1)当派生类中不含对象成员时
●在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→基类的析构函数。
(2)当派生类中含有对象成员时
●在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。
#include<iostream>#include<iomanip>using namespace std ;class Point{public:Point( int = 0, int = 0 ) ; // 带默认参数的构造函数void setPoint( int, int ) ; // 对点坐标数据赋值int getX() const{return x ;}int getY() const{return y ;}friend ostream&operator<< (ostream &, const Point &);protected:int x, y; // Point类的数据成员};class Circle : public Point{public:Circle(double r=0.0, int x=0,int y=0); // 构造函数void setRadius(double); //置半径*/double getRadius() const; //返回半径double area() const; // 返回面积friend ostream&operator<< (ostream &, const Circle &); //友元函数protected:double radius; // 数据成员,半径};class Cylinder:public Circle{public:Cylinder(double h=0.0, doubler=0.0, int x=0, int y=0); //构造函数void setHeight(double); //置高度值double getHeight() const; //返回高度值double area() const; //返回面积double volume() const; //返回体积friend ostream &operator<<(ostream &, const Cylinder &); //友元函数protected:double height; // 数据成员,高度};// Point 类的成员函数// 构造函数,调用成员函数对 x,y作初始化Point::Point ( int a, int b ){setPoint ( a, b ) ;}// 对数据成员置值void Point :: setPoint ( int a, int b ){x = a ;y = b ;}// 重载插入算符,输出对象数据ostream &operator<< ( ostream &output, const Point &p ){output << '[' <<p.x << "," << p.y << "]" ;return output ;}// Circle 类的成员函数// 带初始化式构造函数,首先调用基类构造函数Circle::Circle( double r, int a, int b ): Point( a, b ){setRadius ( r );}// 对半径置值void Circle::setRadius ( double r ){radius = ( r >= 0 ? r : 0 );}// 返回半径值double Circle::getRadius() const{return radius;}// 计算并返回面积值double Circle::area() const{return 3.14159 * radius * radius ;}// 输出圆心坐标和半径值ostream & operator<< ( ostream &output, const Circle&c){output << "Center =" << '[' << c.x << "," << c.y <<"]" << "; Radius = "<<setiosflags(ios::fixed|ios::showpoint) << setprecision(2) <<c.radius ;return output ;}Cylinder::Cylinder(double h, double r, int x, int y):Circle(r,x,y){setHeight(h);}void Cylinder::setHeight(double h){height = ( h >= 0 ? h : 0 );}double Cylinder::getHeight() const{return height;}double Cylinder::area()const{return 2*Circle::area()+2*3.14159*radius*height;}double Cylinder::volume() const{return Circle::area()*height;}ostream &operator<< ( ostream &output, const Cylinder&cy ){output << "Center =" << '[' << cy.x << "," << cy.y <<"]" << "; Radius = "<<setiosflags(ios::fixed|ios::showpoint) << setprecision(2) <<cy.radius<< "; Height= " << cy.height << endl ;return output;}int main(){Point p ( 72, 115 ) ; //定义点对象并初始化cout << "The initiallocation of p is " << p << endl ;p.setPoint ( 10, 10 ) ; //置点的新数据值cout << "\nThe newlocation of p is " << p << endl ; //输出数据Circle c ( 2.5, 37, 43 ) ; //定义圆对象并初始化cout<<"\nThe initiallocation and radius of c are\n"<<c<<"\nArea ="<<c.area()<<"\n" ;c.setRadius ( 4.25 ) ;c.setPoint ( 2, 2 ) ;cout<<"\nThe newlocation and radius of c are\n"<<c<<"\nArea ="<<c.area()<< "\n" ;Cylinder cyl ( 5.7, 2.5, 12, 23) ; //定义圆柱体对象并初始化}
多继承
一个类有多个直接基类的继承关系称为多继承
多继承声明语法
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n
{
数据成员和成员函数声明
};
多继承的派生类构造和访问
多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员。
执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。
多继承的构造函数
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}
多继承方式下构造函数的执行顺序:
●先执行所有基类的构造函数
●再执行对象成员的构造函数
●最后执行派生类的构造函数
处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序。
与派生类构造函数中所定义的成员初始化列表顺序没有关系。
内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致。
多继承方式下析构函数的执行顺序;
●析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与基类中的析构函数的定义方式完全相同。
●功能是在派生类中对新增的有关成员进行必要的清理工作。
●析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理。
★复制兼容规则
●赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
派生类->基类(大->小)
//派生类
AdminCheckIn a;
//基类
CheckIn *c1=new UserCheckin();//赋值兼容规则;
CheckIn &c2=a; //赋值兼容规则;
赋值兼容规则中所指的替代包括以下的情况:
a 派生类的对象可以赋给基类对象
b 派生类的对象可以初始化基类的引用
c 派生类的对象的地址可以赋给基类类型的指针
可行性
通过公有继承,
l 派生类得到了除了构造、析构函数以外的所有成员
l 且这些成员的访问控制属性也和基类完全相同。
l 这样,它便具备了基类的所有功能。
利用赋值兼容规则
l 派生类的对象可以赋给基类对象(强制类型转换)
l 派生类的对象可以初始化基类的引用
l 派生类的对象的地址可以赋给基类类型的指针
★注意的问题
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。
重要知识总结
继承可以传递,但是继承时单项的。
多继承的容易出现二义性,慎用。
私有成员不被继承,不能被派生类使用。
学西心得:
继承最大的特点就是代码重用,让代码变得简洁。
继承的学习貌似让我打开了另一扇窗户,换一种角度思考问题,采用新知识的方法解决旧问题,用一种简单的方法去解决复杂的问题。
继承的可以说让我在写代码时又了规范性,不在随便的命名,有意识的相似功能的函数,明明相似,目的就是为了可以使用继承,优化代码。
继承的学习让我又积累了优化代码的方法。
继承的学习总结与感悟相关推荐
- 有意义的人生与学习——跟随大师感悟人生
有意义的人生与学习--跟随大师感悟人生 最值得学习的东西之一是:归根结底,自己要对自己负责.不要怨天尤人,而应自我主宰.人们只有在小我之外承担更大的义务,肯为人类.为工作.为某种信念而献身,才能使得自 ...
- 2011学习总结(二)感悟篇
2011学习总结(二)感悟篇 2011年12月18日 懒了两天 今天才想起来要动笔 今天去了同事推荐的狗肉馆去喝了一碗汤 自己去的 味道是不错的 但是突然感觉没有在二七广场的时候喝的那么带劲 原因?很 ...
- 《数字图像处理》学习总结及感悟:第二章数字图像基础(2)电磁波、传感器及辐射成像原理
☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 本系列文章记录老猿自学冈萨雷斯<数字图像处理>的感悟和总结,不过 ...
- 无人驾驶学习介绍和感悟
什么是无人驾驶? 近两年来随着无人驾驶技术突飞猛进,各大整车企业.无人驾驶系统解决方案提供商(如百度阿波罗.景驰等)也在不断努力,以将无人驾驶技术向商业化落地推进.显然,无人驾驶技术已经不再是遥不可及 ...
- 《数字图像处理》学习总结及感悟:第二章数字图像基础(4)像素间的关系
☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 本系列文章记录老猿自学冈萨雷斯<数字图像处理>的感悟和总结,不过 ...
- 《数字图像处理》学习总结及感悟:第二章数字图像基础(1)人眼结构、感知和错觉
☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 本系列文章记录老猿自学冈萨雷斯<数字图像处理>的感悟和总结,不过 ...
- 总结自己大学学习Java过程感悟
1.首先明白自己为什么学这个专业 2.明确以后的就业方向,和就业前景 3.找到适合自己的位置 4.坚持不懈,Java学习不能急躁,稳得住,耐的了无聊. 这里我们就正式开始吧. 所有编程语言的学习都是从 ...
- 冈萨雷斯《数字图像处理》学习总结及感悟:第一章 绪论 百闻不如一见
☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 好几月前开始自学OpenCV-Python,但老猿以前没接触过图像基础知识, ...
- 关于在大学中如何学习的一点感悟
一直以来中国教育以"填鸭"式的教学风格而闻名,最近在看了几本英文翻译过来的书后特别有感触. 甭管学生想不想学,学了能不能消化,学了会不会用,只是一味的教授知识. 上了(6+3+3+ ...
最新文章
- 13个风格独特的关于页面(About Pages)设计
- python学习(1)
- kettle根据参数动态派生列
- 怎么让电脑屏幕一直亮着_电视机指示灯亮不开机怎么办 电视机故障及维修介绍【详解】...
- docker php gd png.h,docker php 容器安装GD库
- vue 切换页面没有改变滚动条_Web前端高级Vue学习笔记(三)
- CodeIgniter的伪静态配置
- 老男孩高端linux培训2014业务服务范围
- Webrtc 开源代码的简单介绍
- Windows下配置安装Git(一)
- 验证服务器的通用性,通用VNFM部署的可行性分析与验证
- Windows server 2012 R2 无法安装vc2015
- SQL 循环语句 while 介绍 实例
- 小程序中新版本的获取用户头像与昵称:bind:chooseavatar
- 对蜜蜂CCD原因调查
- Android大赛首轮获奖作品解析
- ac1900修改代理服务器,AC1900路由器怎么设置? | 192路由网
- ibm服务器安装ghostxp系统,windows xpsp3 ghost安装最实用的方法
- 通过用户id 查询权限
- sqlserver数据库优化指南
热门文章
- C1认证学习十(Ipv6)
- 设计师必备!超好用的MAC电脑网页设计师软件
- 2018年第九届蓝桥杯真题解析 | 日志统计【Python】
- kubectl命令补全出错:kubectl ge-bash: _get_comp_words_by_ref: command not found
- echarts 好看的柱形图
- 新买的苹果11如何验真假
- 清华大学计算机专业课参考书,清华大学计算机考研辅导班:专业考研参考书目...
- 用Python做一个简陋的文本编辑器
- 【项目】智能WiFi远程灯光控制系统
- 方舟手游服务器gg修改,【修改贴】关于单机版gg修改器的应用。