一、继承和派生的基本概念

从一个或多个以前定义的类(基类)产生新类的过程称为派生,这个新类称为派生类。基类和派生类又可以分别叫做父类和子类。派生的新类可以增加或重新定义数据和操作,这就产生了类的层次性。

类的继承是指派生类继承基类的数据成员和成员函数。继承通常用来表示类属关系,不能将继承理解为构成关系。当从现有类中派生出新类时,派生类可以有以下几种变化:

① 增加新的成员;

② 重新定义已有的成员函数;

③ 改变基类成员的访问权限。

C++中有两种继承:单一继承和多重继承。

二、单一继承

1、派生类的构造函数和析构函数

先看一个简单示例:

#include <iostream>
using std::cout;class Point {
private:int x, y;
public:Point(int a, int b) {x = a;y = b;cout << "正在初始化Point类的对象" << "\n";}~Point() {cout << "销毁Point类的对象" << "\n";}void showXY() {cout << "x=" << x << ",y=" << y << "\n";}
};class Rectangle: public Point {//派生类继承基类并使用访问控制
private:int width, height;
public:Rectangle(int a, int b, int c, int d) :Point(a, b) {//定义派生类的构造函数width = c;height = d;cout << "正在初始化Rectangle类" << "\n";}~Rectangle() {cout << "销毁Rectangle类的对象" << "\n";}void showWH() {cout << "W=" << width << ",H=" << height << "\n";}
};
#include "Example1.h"
void example1();int main() {example1();return 0;
}void example1() {Rectangle r(1, 2, 3, 4);r.showWH(); //调用派生类的成员函数r.showXY(); //调用基类的成员函数
//  正在初始化Point类的对象
//  正在初始化Rectangle类
//  W=3,H=4
//  x=1,y=2
//  销毁Rectangle类的对象
//  销毁Point类的对象
}

派生类的声明要在类名后添加一个冒号,冒号后面是访问控制关键字(public、protected、private),之后是基类名。 由于构造函数是不被继承的,所以一个派生类只能调用它的直接基类的构造函数,对基类成员进行初始化,然后执行派生类的构造函数。如果一个基类依是一个派生类,则这具过程递归进行,当该对象消失时,析构函数的执行顺序和执行构造函数时的顺序正好相反。

2、类的保护成员

关键字protected之后声明的是类的保护成员,保护成员具有私有成员和公有成员的双重角色,对派生类的成员函数而言,它是公有成员可以被访问;对其他函数而言则仍是私有成员,不能被访问。示例如下:

#include<iostream>
using std::cout;class Point2{
protected:int x,y;
public:Point2(int a,int b){x = a;y = b;}void show(){cout << "x=" << x << ",y=" << y << "\n";}
};class Rectangle2: public Point2{
protected:int width,height;
public:Rectangle2(int a,int b,int c,int d):Point2(a,b){width = c;height = d;}void show(){cout << "x=" << x << ",y=" << y << ",width=" << width << ",height=" << height << "\n";}
};
#include "Example2.h"
void example2();
int main() {example2();return 0;
}void example2(){Point2 a(1,2);Rectangle2 b(10,20,30,40);a.show();b.show();
//  x=1,y=2
//  x=10,y=20,width=30,height=40
}

上例中,派生类虽然继承了基类的成员函数show(),但它改造了这个函数,使函数能显示所有数据,这并不会影响基类函数原来的功能。

3、访问权限和赋值兼容规则

⑴ 公有派生和赋值兼容规则

在公共派生情况下,基类成员的访问权限在派生类中保持不变。所谓兼容规则是指在公有派生情况下,一个派生类的对象可以作为基类的对象来使用的情况。示例如下:

#include <iostream>
#include <string>
using std::cout;
using std::string;class Dog{
protected:int age;
public:Dog(int a){age = a;}void show(){cout << "狗狗" << age << "岁了" << "\n";}
};class WhiteDog : public Dog{
private:string color;
public:WhiteDog(int a,string b):Dog(a){color = b;}void show(){cout << color << "狗狗" << age << "岁了" << "\n";}
};
#include "Example3.h"
void example3();
int main() {example3();return 0;
}void example3() {WhiteDog wd(1, "白色");Dog d(2);wd.show();//白色狗狗1岁了d.show();//狗狗2岁了//派生类的对象初始化基类的引用Dog &d1 = wd;d1.show();//狗狗1岁了,实际调用的是基类的show函数//派生类对象的地址赋给指向基类的指针Dog *d2 = &wd;d2->show();//狗狗1岁了,实际调用的是基类的show函数WhiteDog *wd1 = &wd;wd1->show();//白色狗狗1岁了,调用的是派生类的show函数//派生类的对象赋给基类的对象d = wd;d.show();//狗狗1岁了,派生类对象的属性值更新基类对象的属性值}

注意:静态成员可以被继承,这时基类对象和派生类对象共享该静态成员。

⑵ is-a和has-a的区别

前者属于继承和派生问题,后者是一个类使用另一个类的问题,即把一个类的对象作为自己的数据成员或者成员函数的参数。

⑶ 私有派生

通过私有派生,基类的私有和不可访问的成员在派生类中是不可访问的,而公有和保护成员这时就成了派生类的私有成员,派生类的对象不能访问继承的基类成员,必须定义公有的成员函数作为接口。更重要的是,虽然派生类的成员函数可通过自定义的函数访问基类的成员,但将该派生类作为基类再进行派生时,这时即使使用公有派生,原基类公有成员在新的派生类中也是不可访问的。示例如下:

#include<iostream>
using std::cout;class Point3{
protected:int x,y;
public:Point3(int a,int b){x = a;y = b;}void show(){cout << "x=" << x << ",y=" << y << "\n";}
};class Rectangle3: private Point3{
private:int width,height;
public:Rectangle3(int a,int b,int c,int d):Point3(a,b){width = c;height = d;}void show(){cout << "x=" << x << ",y=" << y << ",width=" << width << ",height=" << height << "\n";}
};class Test3:public Rectangle3{
public:Test3(int a,int b,int c,int d):Rectangle3(a,b,c,d){}void show(){//Point3::show();//私有派生时,不能使用此方式调用该函数Rectangle3::show();}
};
#include "Example4.h"
void example4();
int main() {example4();return 0;
}void example4(){//实例化一个Test3类的对象Test3 t(1,2,3,4);t.show();//x=1,y=2,width=3,height=4
}

⑷ 保护派生

派生也可以使用protected,这种派生使原来的权限都降一级使用,即private变为不可访问,protected变为private,public变为protected。

三、多重继承

继承的规则是一样的,只不过继承的时候需要用逗号分隔。示例如下:

#include<iostream>
using std::cout;class A{
private:int a;
public:void setA(int i){a = i;}void showA(){cout << "a=" << a << "\n";}
};class B{
private:int b;
public:void setB(int t){b = t;}void showB(){cout << "b=" << b << "\n";}
};class C:public A,private B{//多继承使用逗号分隔
private:int c;
public:void setC(int x,int y){c = x ;setB(y);}void showC(){cout << "c=" << c << "\n";showB();}
};
#include "Example5.h"
void example5();
int main() {example5();return 0;
}void example5(){C c;c.setA(10);c.showA();//a=10c.setC(20,30);c.showC();//c=20 b=30
}

四、二义性及其支配规则

对基类成员的访问必须是无二义性的,如果使用一个表达式的含义能解释为访问多个基类中的成员,则这种对基类成员的访问就是不确定的,称这种访问具有二义性。

1、作用域分辨符和成员名限定

从类中派生其他类可能导致几个类使用同一个成员函数名或数据成员名时,程序必须确切的告诉编译器使用哪个版本的数据成员或成员函数。示例如下:

#include <iostream>
using std::cout;class A1{
public:void show(){cout << "a1.show" << "\n";}
};class B1{
public:void show(){cout << "b1.show" << "\n";}void display(){cout << "b1.display" << "\n";}
};class C1:public A1,public B1{
public:void display(){cout << "c1.display" << "\n";}void reveal(){//show();//具有二义性}void reveal1(){A1::show();//使用基类的函数}void reveal2(){B1::show();//使用基类的函数}
};
#include "Example7.h"
void example7();
int main() {example7();return 0;
}void example7(){C1 obj;obj.A1::show();//a1.showobj.B1::show();//b1.showobj.B1::display();//b1.displayobj.C1::display();//c1.displayobj.display();//c1.displayobj.reveal1();//a1.showobj.reveal2();//b1.show
}

由上例可知,如果基类中的名字在派生类中再次声明,则派生类中的名字就隐藏了基类中的相应名字,C++可以使用作用域分辨运算符“::”来存取那些被隐藏的名字,这一过程叫做作用域分辨。

2、派生类支配基类的同名函数

基类的成员和派生类新增的成员都具有类作用域,基类在外层,派生类在内层。如果这时派生类定义了一个和基类成员函数同名的新成员函数(同名的非重载函数),派生类的新成员函数就覆盖了外层的同名成员函数。在这种情况下,直接使用成员名只能访问派生类的成员函数,只有使用作用域分辨,才能访问基类的同名成员函数。

由于二义性原因,一个类不能从同一个类中直接继承一次以上,如果必须要这样做,可以使用一个中间类,二义性检查是在访问权限检查之前进行的,因此成员的访问权限不能解决二义性问题。如果涉及几层继承关系,对于任一基类中可以存取的成员,都可以通过作用域分辨进行存取。一般只有派生类中使用的标识符与基类中的标识符同名时,才有必要使用作用域分辨符进行存取。

五、友元与派生

友元声明与访问控制无关,友元声明在私有区域进行或在公有区域进行是没有太大区别的,对友元函数的唯一限制是该函数必须出现在类声明内的某一部分。

友元关系是无等级的,友元可以访问任何一个类成员函数可以访问的对象,这比一个派生类可以访问的对象还多。友元关系不能继承,一个派生类的友元只能访问该派生类的直接基类的公有和保护成员,不能访问私有成员。当友元访问直接基类的静态保护成员时,只能使用对象名而不能使用成员名限定。

C++程序设计(六)—— 继承和派生相关推荐

  1. 【学习笔记】C++语言程序设计(郑莉):继承与派生

    [学习笔记]C++语言程序设计(郑莉):继承与派生 1. 类的继承与派生 1.1 派生类的定义 1.2 派生类生成过程 2. 访问控制 3. 类型兼容规则 4. 派生类的构造和析构函数 4.1 构造函 ...

  2. 程序设计与算法三~C++面向对象程序设计~北大郭炜MOOC学习笔记~第五章:继承与派生(新标准C++程序设计)

    以下内容为笔者手打,望读者珍惜,如有转载还请注明. 第五章 继承与派生 $5.1 继承与派生的概念 $5.1.1 基本概念     在C++中,当定义一个新的类B时,如果发现类B拥有某个已经写好的类A ...

  3. C++类的继承与派生

    文章目录 一.继承 二.基类和派生类 三.继承方式 六.多继承 七.应用经验 八.课后作业 九.版权声明 一.继承 继承是面向对象程序设计中最重要的一个概念.继承允许我们根据一个类来定义另一个类,达到 ...

  4. C++语言类的继承与派生介绍和示例

    继承 继承是面向对象程序设计中最重要的一个概念.继承允许我们根据一个类来定义另一个类,达到了代码功能重用效果. 当创建一个类时,如果待创建的类与另一个类存在某些共同特征,程序员不需要全部重新编写成员变 ...

  5. c++学习---继承与派生类

    1.继承 继承性是面向对象程序设计当中最重要的机制,这种机制可以无限度的重复利用程序的 资源.通过C++语言中的继承机制,我们可以扩充和完善旧的程序设计以适应新的需求. 这样不仅可以节省程序开发的时间 ...

  6. 【C++入门】C++ 继承和派生

    C++继承和派生 文章目录 C++继承和派生 一.继承和派生的概念 1.继承的概念 2.派生类 3.继承实例程序 二.类之间的两种关系 1.继承关系 2.复合关系 三.派生类覆盖基类成员&类的 ...

  7. 实验四 类和对象;类的继承和派生;多态性; 接口;构造器应用

    实验四 类和对象:类的继承和派生:多态性: 接口:构造器应用 一.实验目的 1. 掌握类与对象的关系: 2. 掌握类的定义: 3. 掌握对象的声明及使用: 4. 掌握构造方法的概念及调用时机: 5. ...

  8. c++的继承与派生之从入门到入坟-------集大成者

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 学习目标 一.为什么使用继承 二.继承的工作方式 1.从基类中派生新类 2.继承下的访问控制 三.派生类对象的初始化和 ...

  9. ☆ C++ 继承与派生(包括虚继承)

    在友元类中我们知道,一旦在一个类中声明了友元类,那么友元类便拥有了访问该类的所有权限,可以在自己的类中对声明自己的类进行一系列操作. 友元类主要目的是为了拓展友元类的功能,但是友元类的权限未免太多了, ...

最新文章

  1. python scrapy 入门,10分钟完成一个爬虫
  2. JAVA NIO存在的问题
  3. 数据库学习day_03:关联关系/ 关联查询/ JDBC
  4. 计算机网络 公有IP和私有IP介绍
  5. XmlSerializer 对象的Xml序列化和反序列化,XMLROOT别名设置
  6. 关于myeclipse打开jsp巨慢解决方案
  7. Mr.J -- yield关键字生成器产生值
  8. IP计算机取证,计算机取证1资料.doc
  9. MFC开发- string、const char*、 char* 、char[]相互转换(全)
  10. CRNN+CTC (基于CTPN 的end-to-end OCR)
  11. java restful文件传输_Spring Boot 2.0实现基于Restful风格的文件上传与下载APIs
  12. PDF文档编辑Acrobat Pro DC
  13. linux图形显卡驱动r600,R600/R700八款显卡Mesa开源驱动性能简测
  14. Kaggle经典项目——房价预测
  15. [原创] Bandwagon 追加 swap 大小
  16. java中retry的使用
  17. » 用HSCALE实现MySQL的数据分布式存储 江边潮未尽,枫红一季秋 -- NinGoo's blog
  18. mysql握手_详细介绍mysql 协议的服务端握手包及对其解析
  19. 利用c51进行数模转换并在液晶屏上显示_单片机数模转换
  20. openlayers+vue 仿百度罗盘功能(指北针)

热门文章

  1. 【U8+】win10/11系统注册用友U8硬加密
  2. qq浏览器tv版 v1.0 官方版
  3. MySQL中的“DATE_SUB()” 函数从日期减去指定的时间间隔
  4. Gnuplot特殊字符之Symbol字体
  5. 【校招VIP】前端JS语言之语法相关
  6. 计算机排查方法,电脑主板故障排查方法
  7. 电脑蓝屏critical_process_died,重装win10遇到的问题
  8. Cortex-A8处理器编程(上)
  9. 一些计算机模拟人脑项目
  10. 业务中继承关系研究(数据库)