[C++]类的继承与派生
继承性是面向对象程序设计的第二大特性,它允许在既有类的基础上创建新类,新类可以继承既有类的数据成员和成员函数,可以添加自己特有的数据成员和成员函数,还可以对既有类中的成员函数重新定义。利用类的继承和派生实现了更高层次的代码可重用性,符合现代软件开发的思想。
C++语言同时支持单一继承和多重继承。单一继承是指派生类只从一个基类继承而来;相应的,多重继承指派生类同时从两个或更多的基类继承而来。java只支持单一继承。
一. 派生类
派生类的定义格式如下:
class <派生类名>:[继承方式]<基类名1>
[,[继承方式]<基类名2>,...,[继承方式]<基类名n>]
{
<派生类新增的数据成员和成员函数定义>
};
说明:
(1)定义派生类关键字可以是class或者是struct,两者区别是:用class定义派生类,默认的继承方式是private,用struct定义派生类,默认的继承方式为public。新增加的成员默认属性也是class对应private属性,struct对应public属性。
(2)基类不能被派生类继承的两类函数是构造函数和析构函数。
二. 3种继承方式下基类成员在派生类中的访问属性
用下面的代码简单理解一下:
#include "stdafx.h"
#include<iostream>
using namespace std; class Base
{
private:
int priData;
protected:
int proData;
public:
int pubData;
}; class D1:private Base//私有继承
{
void f1()
{
//priData=1;//基类private成员在派生类中不可直接访问
proData=;//基类的protected成员在派生类中为private访问属性
pubData=;//基类的public成员在派生类中为private访问属性
}
}; class D2:protected Base//保护继承
{
void f2()
{
//priData=1;//基类private成员在派生类中不可直接访问
proData=;//基类的protected成员在派生类中为protected访问属性
pubData=;//基类的public成员在派生类中为protected访问属性
}
}; class D3:public Base//公有继承
{
void f3()
{
//priData=1;//基类private成员在派生类中不可直接访问
proData=;//基类的protected成员在派生类中为protected访问属性
pubData=;//基类的public成员在派生类中为public访问属性
}
}; int main()
{
Base obj;
//obj.priData=1;//对象不可访问Base类中private成员
//obj.proData=2;//对象不可访问Base类中protected成员
obj.pubData=;
D1 objD1;
//objD1.pubData=3;//private属性,不可访问
D2 objD2;
//objD2.pubData=3;//protected属性,不可访问
D3 objD3;
objD3.pubData=;//public属性,可以访问
return ;
}
基类的private成员函数虽然在派生类的成员函数中不可直接访问,但派生类的成员函数可以通过调用基类被继承的函数来间接访问这些成员。如果基类的函数被继承后在派生类中仍为public成员,则可以通过派生类对象直接调用。
先来看一下类成员的访问属性及作用:
访问属性 | 作用 |
private | 只允许该类的成员函数及友元函数访问,不能被其他函数访问 |
protected | 既允许该类的成员函数及友元函数访问,也允许其派生类的成员函数访问 |
public | 既允许该类的成员函数访问,也允许类外部的其他函数访问 |
好了,继续通过代码来理解:
#include "stdafx.h"
#include<iostream>
using namespace std; class Base
{
private:
int priData;
protected:
int proData;
public:
int pubData;
//在类的定义中不能对数据成员进行初始化
void SetData()//为基类中的数据成员赋值
{
priData=;
proData=;
pubData=;
}
void Print()
{
cout<<"priData="<<priData<<endl;
cout<<"proData="<<proData<<endl;
cout<<"pubData="<<pubData<<endl;
}
}; class Derived:public Base
{
public:
void ChangeData()
{
SetData();
proData=;//在派生类的成员函数类可以访问基类的非私有成员
}
}; int main()
{
Base b;
b.SetData();
b.Print(); Derived d1;
d1.ChangeData();
d1.pubData=;
d1.Print(); return ;
}
程序运行结果如下:
三. 派生类的构造函数和析构函数
在定义一个派生类的对象时,在派生类中新增加的数据成员当然用派生类的构造函数初始化,但是对于从基类继承来的数据成员的初始化工作就必须由基类的构造函数完成,这就需要在派生类的构造函数中完成对基类构造函数的调用。同样,派生类的析构函数值能完成派生类中新增加数据成员的扫尾、清理工作,而从基类继承来的数据成员的扫尾工作也应有基类的析构函数完成。由于析构函数不能带参数,因此派生类的析构函数默认直接调用了基类的析构函数。
派生类构造函数定义格式如下:
<派生类名>(<总形式参数表>):<基类名1>(<参数表1>),
<基类名2>(<参数表2>),[...,<基类名n>(<参数表n>),其他初始化项>]
{
[<派生类自身数据成员的初始化>]
}
说明:
(1)总形式表给出派生类构造函数中所有的形式参数,作为调用基类带参构造函数的实际参数以及初始化本类数据成员的参数;
(2)一般情况下,基类名后面的参数表中的实际参数来自前面派生类构造函数形式参数总表,当然也可能是与前面形式参数无关的常量;
(3)在多层次继承中,每一个派生类只需要负责向直接基类的构造函数提供参数;如果一个基类有多个派生类,则每个派生类都要负责向该积累的构造函数提供参数。
1.单一继承
#include"stdafx.h"
#include<iostream>
using namespace std; class Other
{
public:
Other()
{
cout<<"constructing Other class"<<endl;
}
~Other()
{
cout<<"destructing Other class"<<endl;
}
}; class Base
{
public:
Base()
{
cout<<"constructing Base class"<<endl;
}
~Base()
{
cout<<"destructing Base class"<<endl;
}
}; class Derive:public Base
{
private:
Other ot;
public:
Derive()
{
cout<<"constructing Derive class"<<endl;
}
~Derive()
{
cout<<"destructing Derive class"<<endl;
}
}; int main()
{
Derive d; return ;
}
程序运行结果如下:
可以看到定义派生类对象时,构造函数的调用顺序:
a.先调用基类的构造函数
b.然后调用派生类对象成员所属类的构造函数(如果有对象成员)
c.最后调用派生类的构造函数
析构函数的调用顺序正好与构造函数调用顺序相反。
2.多重继承
#include"stdafx.h"
#include<iostream>
using namespace std; class Grand
{
int g;
public:
Grand(int n):g(n)
{
cout<<"Constructor of class Grand g="<<g<<endl;
}
~Grand()
{
cout<<"Destructor of class Grand"<<endl;
}
}; class Father:public Grand
{
int f;
public:
Father(int n1,int n2):Grand(n2),f(n1)
{
cout<<"Constructor of class Father f="<<f<<endl;
}
~Father()
{
cout<<"Destructor of class Father"<<endl;
}
}; class Mother
{
int m;
public:
Mother(int n):m(n)
{
cout<<"Constructor of class Mother m="<<m<<endl;
}
~Mother()
{
cout<<"Destructor of class Mother"<<endl;
}
}; class Son:public Father,public Mother
{
int s;
public:
Son(int n1,int n2,int n3,int n4):Mother(n2),Father(n3,n4),s(n1)
{
cout<<"Constructor of class Son s="<<s<<endl;
}
~Son()
{
cout<<"Destructor of class Son"<<endl;
}
}; int main()
{
Son s(,,,);
return ;
}
程序运行结果如下:
可以看到,与单一继承不同的是:在多重继承中,派生类有多个平行的基类,这些处于同一层次的基类构造函数的调用顺序,取决于声明派生类时所指定的各个基类的顺序,而与派生类构造函数的成员初始化列表中调用基类构造函数的顺序无关。
[C++]类的继承与派生的更多相关文章
- 模块的封装之C语言类的继承和派生
[交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我 ...
- 09--c++ 类的继承与派生
c++ 类的继承与派生 一.基本概念 1.类的继承,是新的类从已有类那里得到已有的特性.或从已有类产生新类的过程就是类的派生.原有的类称为基类或父类,产生的新类称为派生类或子类. 2.派生类的 ...
- C++学习笔记:07 类的继承与派生
课程<C++语言程序设计进阶>清华大学 郑莉老师) 基本概念 继承与派生的区别: 继承:保持已有类的特性而构造新类的过程称为继承. 派生:在已有类的基础上新增自己的特性(函数方法.数据成员 ...
- Day 5-2 类的继承和派生,重用
类的继承 派生 在子类中重用父类 组合 抽象类 定义: 继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创建新类的方式,在python中,新 ...
- 4-13 object类,继承和派生( super) ,钻石继承方法
1,object 类 object class A: ''' 这是一个类 ''' pass a = A() print(A.__dict__) # 双下方法 魔术方法 创建一个空对象 调用init方法 ...
- Python基础(16)_面向对象程序设计(类、继承、派生、组合、接口)
一.面向过程程序设计与面向对象程序设计 面向过程的程序设计:核心是过程,过程就解决问题的步骤,基于该思想设计程序就像是在设计一条流水线,是一种机械式的思维方式 优点:复杂的问题的简单化,流程化 缺点: ...
- 对C++类的继承和派生的理解
C++中的继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承类似,例如儿子继承父亲的财产. 1.继承(Inheritance)可以理解为一个类从另一个类获取成员变量和成员函数的过程. ...
- Python3 面向对象-类的继承与派生
1.什么是继承? 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类可称为基类或超类,新建的类称为派生类和或子类. 子类会遗传父类的属性,从而解决代码重用问题. ...
- C++——类的继承(派生)
类的继承就是子类可以拥有父类的成员变量和成员函数 //public 修饰的成员变量 方法 在类的内部 类的外部都能使用//protected: 修饰的成员变量方法,在类的内部使用 ,在继承的子类中可用 ...
随机推荐
- 快速同步mysql数据到redis中
MYSQL快速同步数据到Redis 举例场景:存储游戏玩家的任务数据,游戏服务器启动时将mysql中玩家的数据同步到redis中. 从MySQL中将数据导入到Redis的Hash结构中.当然,最直接的 ...
- 信号驱动的IO
(1)client1,基于SIGIO的写法: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h ...
- Mybatis动态SQL
1.动态SQL基本标签 •if •choose (when, otherwise) •trim (where, set) •foreach 2.IF 具体用法 <select id=" ...
- angularjs+nodejs+mongodb三件套
说实话,自己对于web前段的认识并不是太深入,但是因为项目的需要,所以有的时候肯定会需要接触到web前段的知识点.说到web前端想必大家肯定会想到css+js+html,的确web前端的工作,从某总角 ...
- AngularJS与RequireJS集成方案
关于angularjs.requirejs的基础知识请自行学习 一.简单事例的项目目录如下: -index.html -scripts文件夹 --controller文件夹 --- mianContr ...
- OC中的消息传递和初始化
[receiver message]:[接收者 消息] 把消息传递给接收者. getter(接收器),setter(设置器):只设置和读取一个参数. Person *p1 = [[Person all ...
- 12.Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings report for details
解释:对于一些管脚,缺少了部分描述,需要再添加一些设置,比如current strength,slew rate等: 措施:打开pin plannel界面,在current strength和slew ...
- VIM技巧:翻页
整页翻页 ctrl-f ctrl-bf=forword b=backward 翻半页ctrl-d ctlr-ud=down u=up 滚一行ctrl-e ctrl-y zz 让光标所在的行居屏幕中央z ...
- Careercup - Microsoft面试题 - 5718181884723200
2014-05-11 05:55 题目链接 原题: difference between thread and process. 题目:请描述进程和线程的区别. 解法:操作系统理论题.标准答案在恐龙书 ...
- Jquery方法的应用
<body> <div id="one"><span>one</span></div><div class=&qu ...