C++ 学习之路(10):继承与派生
一、
i.类的继承:新的类从已有类那里得到已有的特性.
II.类的派生:从已有类产生新类的过程.
iii.已有类成为 基类 或 父类.
iv.产生的新类成为 派生类 或 子类.
二、为什么要使用继承?
已有学生类Student含有number(学号)、name(姓名)、score(成绩)等数据成员与成员函数print
现需要声明一个大学生类UStudent,它含有number(学号)、name(姓名)、score(成绩)以及major(专业)等数据成员与成员函数print1();
如果要重写一个大学生类,则这两个类都代码严重重复,为了提高代码的重用性,可以引入继承。
三、派生类的声明
class 派生类名:[继承方式] 基类名
{
派生类新增的数据成员和成员函数;
};
i.基类名:一个已经声明都类的名称
ii.派生类名:继承原有类的特性而生成的新类的名称.
III.继承方式:规定了如何访问从基类继承的成员,可以是关键字private、protected或public,分别表示私有继承、保护继承和公有继承.
注:如果不显式地给出继承方式关键字,系统默认为私有继承(private).
iv.类的继承方式制定了派生类成员以及类外对象对于从基类继承来的成员的访问权
四、基类成员在派生类中的访问属性
i.派生类可以继承基类中【除了构造函数与析构函数之外】的成员.
ii.基类成员在派生类中的访问属性(如表)
归纳得:
①基类中的私有成员.
无论哪种继承方式,基类中的私有成员不允许派生类的继承,即在派生类中是不可直接访问的.
②基类中的公有成员.
公有继承时,基类中的所有公有成员在派生类中仍以公有成员的身份出现的.
私有继承时,基类中的所有私有成员在派生类中都是以私有成员的身份出现的.
保护继承时,基类中的所有保护成员在派生类中都是以保护成员的身份出现的.
③基类中都保护成员.
公有继承时,基类中的所有保护成员在派生类中仍以保护成员的身份出现的.
私有继承时,基类中的所有保护成员在派生类中都是以私有成员的身份出现的.
保护继承时,基类中的所有保护成员在派生类中仍以保护成员的身份出现的.
五、派生类对基类成员的访问规则
派生类对基类成员的访问形式主要有以下两种:
①内部访问:由派生类中新增的成员函数对基类继承来的成员的访问.
②对象访问:在派生类外部,通过派生类的对象对从基类继承来的成员的访问.
i.私有继承的访问规则(如表)
ii.公有继承都访问规则(如表)
iii.保护继承的访问规则(如表)
六、派生类的构造函数和析构函数
i.简单的派生类的构造函数和析构函数的构造规则
①当基类的构造函数没有参数,或没有显式定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数.
②当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径.
③在C++中,派生类构造函数的一般格式为:
派生类名(参数总表):基类名(参数表)
{
派生类新增数据成员的初始化语句;
}
ii.含有子对象的派生类的构造函数
①子对象:基类的对象
②在C++中,派生类中含有子对象时的构造函数的一般形式为:
派生类名(参数总表):基类名(参数表0),子对象名1(参数表1),...,子对象名n(参数表n)
{
派生类新增成员的初始化语句;
}
③说明:
1、当基类构造函数不带参数时,派生类不一定需要定义构造函数;然而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数,甚至所定义的生类构造函数都函数体可能为空,它仅仅起参数的传递作用.
2、若基类使用默认构造函数或不带参数都构造函数,则在派生类中定义构造函数时可略去“:基类构造函数名(参数表)”,此时若派生类也不需要构造函数,则可不定义造函数.
3、如果派生类的基类也是一个派生类,每个派生类只需负责其直接基类数据成员的初始化,依次上溯.
4、派生类都构造函数可以不显式地写出基类的构造函数.
5、要调用的基类构造函数的参数可在派生类的构造函数中明确地给出.
六、调整基类成员在派生类中的访问属性的其他方法
i.同名成员
派生类可以重新声明与基类成员同名的成员。在没有虚函数的情况下,如果在派生类中定义了与基类成员同名的成员,则称派生类成员覆盖了基类的同名成员此后若要再度使用基类都同名成员则需使用作用域标识符“::”--> 基类名::同名函数.
ii.访问声明
①有时程序员可能希望基类的个别成员能被派生类的对象直接访问,而不是通过派生类的公有成员函数间接访问.为此,C++提供了称为访问声明的特殊机制,可个别调整基类的某些成员在派生类中的访问属性,使之在派生类中保持原来的访问属性.
②访问声明的方法:
把基类的保护成员或公有成员直接写在私有派生类定义式中的同名段中,同时给成员名前冠以基类名和作用于标识符“::”. 例如:基类中的show()以A::show写进派生类B中
③使用访问声明时应注意:
1、数据成员也可以使用访问声明. 例如:A::X1;
2、访问声明中只含不带类型和参数的函数名或变量名.
3、访问声明不能改变成员在基类中的访问属性.
4、对于基类中的重载函数名,访问声明将对基类中所有同名函数起作用。这意味着对于重载函数使用访问声明时要慎重.
七、多继承与虚基类
通常,一个派生类有两个或多个基类,派生类从两个或多个基类中继承所需的属性。
i.声明多继承派生类的一般形式:
class 派生类名:继承方式1 基类名1,...,继承方式i 基类名i ,...,继承方式n 基类名n
{
派生类新增的数据成员和成员函数
};
ii.多继承派生类对基类成员的访问必须是无二义性的.若有二义性,要想办法消除.例如:
obj.X::f();
obj.Y::f();
iii.多继承派生类的构造函数与析构函数
多重继承构造函数定义的一般形式:
派生类名(参数总表) :基类名1(参数表1),...,基类名i(参数表i),...,基类名n(参数表n)
{
派生类新增成员的初始化语句
}
iv.析构函数在多继承派生类里是相互独立的.
虚基类:
i.作用:若果一个类有多个直接基类,而这些直接基类又有一个共同的基类,则在最底层的派生类中会保留这个间接的共同基类数据成员的多份同名成员.因此在访问时可能会产生二义性,虚基类便是为了消除二义性.
ii.声明:
class 派生类名:virtual 继承方式 类名
{ ...... }
iii.虚基类初始化时应注意:
①如果虚基类中定义有带形参的构造函数并没有定义默认形式的构造函数 -->整个继承结构中,所有派生类都必须在构造函数的成员初始化列表中列出对虚基类构造函数的调用,以初始化虚基类中定义的数据成员.
②建立对象时,如果该对象中含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。该派生类的其他基类对虚基类构造函数的调用都自动被忽略.
③若同一层次中包含虚基类和非虚基类,先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类的构造函数。
④对于多个虚基类(或者 非虚基类 ),构造函数的执行顺序仍然是先左右后,自上而下.
⑥若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数.
八、赋值兼容规则
i.赋值兼容:不同类型数据之间的自动转换和赋值. 这种关系在基类和派生类对象之间也存在.
即 基类对象的任何地方,都可以使用其子类对象来替代.
例:类Derived公有继承类Base
① Base b;
Derived d;
b=d; // 用派生类Derived的对象d对基类对象b赋值
②Base b;
Derived d;
Base &br=d; // 用派生类Derived的对象d对基类Base的对象的引用br进行初始化
③ Derived d;
Base *bp = &d; // 把派生类对象的地址&d赋给指向基类的指针bp,也就是说,使指向基类对象的指针bp也可以指向派生类对象d.
④..........
void fun(Base &bb) // 基类的对象作为参数
{ cout<<bb.i<<endl; } // 可以用派生类Derived的对象赋给基类的数据成员i的值
ii.赋值兼容规则举例:
#include <iostream>
using namespace std;// 声明基类Base
class Base
{public:int i;Base (int x) // 基类的构造函数{ i = x; }void disp(){ cout<<"Base "<<i<<endl; }
};// 声明公有派生类Derived
class Derived:public Base
{public:Derived(int x):Base(x) // 派生类的构造函数{ };void disp(){ cout<<"Derived "<<i<<endl; }
};
int main()
{Base b1(11); // 定义基类对象b1b1.disp();Derived d1(22); // 定义派生类对象d1b1 = d1; // 用派生类对象d1给基类对象b1赋值b1.disp();Derived d2(33); // 定义派生类对象d2Base &b2 = d2; // 用派生类对象d2来初始化基类对象的引用b2b2.disp();Derived d3(44); // 定义派生类对象d3Base *b3 = &d3; // 把派生类对象的地址赋给指向基类的指针b3b3->disp();Derived *d4 = new Derived(55); // 定义派生类的无名对象,将该对象的地址// 存放在派生类对象指针d4中Base *b4 = d4; // 把指向派生类对象指针d4的值赋给基类对象指针b4b4->disp();delete d4;return 0;
}
总结:··程序举例
/*语言:C++ 编译环境:Visual C++6.0继承与派生:程序举例声明一个共同的基类Person,它包含了所有派生类共有的数据,职工类Employee和学生类Student为虚基类Person的派生类,在职大学生类E_Student是职工类Employee和学生类Student的共同派生类。每个类定义了一个相对于特定类不同的print()函数,输出各类的数据成员.
*/
#include <iostream>
#include <string>
using namespace std;// 声明基类Person
class Person
{protected:string name; // 姓名char sex; // 性别int age; // 年龄public:Person(string name1, char sex1, int age1) // 构造函数{name = name1;sex = sex1;age = age1;}void print(){cout<<"姓名: "<<name<<endl;cout<<"性别: "<<sex<<endl;cout<<"年龄: "<<age<<endl;}
};// 声明类Person是类Student的虚基类
class Student:virtual public Person
{protected:string major; // 专业public:Student(string name1, char sex1, int age1, string major1): // 构造函数Person(name1,sex1,age1){ major = major1; }void print(){Person::print();cout<<"专业: "<<major<<endl;}
};// 声明类Person是类Employee的虚基类
class Employee:virtual public Person
{protected:string dept; // 部门public:Employee(string name1, char sex1, int age1, string dept1): // 构造函数Person(name1,sex1,age1){ dept = dept1; }void print(){Person::print();cout<<"部门: "<<dept<<endl;}
};// 声明类E_Student为 类Employee 和 类Studnet 的派生类
class E_Student:public Employee,public Student
{public:E_Student(string name1, char sex1, int age1, string major1, string dept1): // 构造函数Person(name1,sex1,age1),Employee(name1,sex1,age1,dept1),Student(name1,sex1,age1,major1){ }void print(){cout<<"姓名: "<<name<<endl;cout<<"性别: "<<sex<<endl;cout<<"年龄: "<<age<<endl;cout<<"部门: "<<dept<<endl;cout<<"专业: "<<major<<endl;}
};int main()
{Student my_Student("李晓敏",'f',22,"应用数学");cout<<"大学生: "<<endl;my_Student.print();cout<<"---------------------------"<<endl;Employee my_Employee("黄百松",'m',55,"科研处");cout<<"职工: "<<endl;my_Employee.print();cout<<"---------------------------"<<endl;E_Student my_E_Student("张大明",'m',35,"教务处","计算机");cout<<"在职大学生: "<<endl;my_E_Student.print();return 0;
}
C++ 学习之路(10):继承与派生相关推荐
- Java学习之路10——多线程
一.线程基本概念 1.Process(进程)与Thread(线程) 说起进程,就不得不说下程序.程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念. 而进程则是执行程序的一次执行过 ...
- GO语言学习之路10
2022/02/01package mainimport ("fmt""strconv""strings" )//1.字符串中常用的系统函数 ...
- Qt学习之路(10): Meta-Object系统
前面说过,Qt使用的是自己的预编译器,它提供了对C++的一种扩展.利用Qt的信号槽机制,就可以把彼此独立的模块相互连接起来,不需要实现知道模块的任何细节. 为了达到这个目的,Qt提出了一个Meta-O ...
- JavaScript仿淘宝京东放大镜效果(鼠标事件)------JavaScript学习之路10
JavaScript仿淘宝京东放大镜效果 注意 一定计算好放大比例,本程序放大5倍,具体放大倍数,自定 效果 完整源码 <!DOCTYPE html> <html lang=&quo ...
- C/C++学习之路: 继承
C/C++学习之路: 继承 目录 继承概述 派生类访问控制 继承中的构造和析构 继承中同名函数的处理方法 非自动继承的函数 继承中静态成员特性 多继承 1. 继承概述 1. 继承基本概念 c++最重要 ...
- C/C++学习----第二章 继承和派生
第二章 继承和派生 2.1 派生 派生类或派生类的使用者均不能访问基类的私有数据成员:对于基类的公有成员的访问,如果派生的方式不同,访问的权限也不同.派生时,不指明派生类型的,按私有派生进行派生. & ...
- c++学习---继承与派生类
1.继承 继承性是面向对象程序设计当中最重要的机制,这种机制可以无限度的重复利用程序的 资源.通过C++语言中的继承机制,我们可以扩充和完善旧的程序设计以适应新的需求. 这样不仅可以节省程序开发的时间 ...
- 【学习笔记】C++语言程序设计(郑莉):继承与派生
[学习笔记]C++语言程序设计(郑莉):继承与派生 1. 类的继承与派生 1.1 派生类的定义 1.2 派生类生成过程 2. 访问控制 3. 类型兼容规则 4. 派生类的构造和析构函数 4.1 构造函 ...
- 程序设计与算法三~C++面向对象程序设计~北大郭炜MOOC学习笔记~第五章:继承与派生(新标准C++程序设计)
以下内容为笔者手打,望读者珍惜,如有转载还请注明. 第五章 继承与派生 $5.1 继承与派生的概念 $5.1.1 基本概念 在C++中,当定义一个新的类B时,如果发现类B拥有某个已经写好的类A ...
- typescript学习之路(四) —— ts类的继承(包含es5以及es6的类继承)
上一文已经写了es5,es6等类的定义,所以本章主要写es5和es6的继承,由于es6的继承和ts的继承如出一辙,只是加了类型定义而已,所以ts的继承稍微写下,不会太详细. 文章目录 es5继承 原型 ...
最新文章
- fflush(stdin)和fflush(stdout)和rewind(stdin)的区别和作用
- 非计算机专业想学习Python,有哪些问题需要注意?
- 三层交换(VLAN间互通+路由功能)+VTP+STP(PVST)综合实验(理论+实践=真实)
- Oracle Study--Oracle RAC CacheFusion(MindMap)
- 光流.flo文件生成.png图片(可批量)
- 洛谷——P1046 [NOIP2005 普及组] 陶陶摘苹果
- python工资一般多少西安-西安Python的就业方向有哪些?
- 推荐系统 | 信息过载的大数据时代,大数据推荐系统如何搭建,趋势何方
- 使用Docker 安装jdk8
- 路由器无线模式与信道检测
- 通过access口加vlan标签吗_如何理解:“Access端口加入的VLAN必须已经存在并且不能是VLAN 1;Hybrid端口加入的VLAN必须已经存在;Trun...
- 米家接入HomeKit系列二:通过群辉NAS的Docker搭建HomeAssistant
- JZ38* 字符串的排列
- Java虚拟机(JVM源码):编译OpenJDK源码
- 常见问题汇总:FLUENT面数据处理
- win10 此电脑中【设备和驱动器】位置出现空白图标
- 用C语言打印简单图案(爱心\圆形\三角形等)
- [笔记]物化视图和普通视图
- 【MacOS】java环境配置
- ThreadPoolExecutor源码走读
热门文章
- 【宋代文学】之《苏洵·六国论》
- Hexo博客搭建、fluid主题美化
- nc608串口服务器型号,NC608R系列 机架型串口服务器
- 2023-07-07-liunx环境,python调用ITK(c++版本)批量生成Drr
- 业余做自由职业,对国外客户如何收款?
- MobTech观察 | “被倒掉的奶”与被忽视的风控
- 关于os.chdir(path)改变工作目录出错问题
- 服务器怎么应急响应,服务器安全事件应急响应排查方法
- 金融工程:绪论中的经典问题
- sqlserver编写通用脚本实现获取一年前日期的方法