C++ 继承和派生介绍
继承(inheritance)是软件重用的一种方式,程序员通过继承可以吸收已有类的数据和行为来创建新类,并可以添加新的数据和行为来增强类的功能。创建新类时,并不需要创建全新的数据和成员函数,我们可以指明这个新类应当继承现有类的成员。此时,现有的类称为“基类”,继承实现的新类称为“派生类”。派生类代表了一组更加特殊化的对象,它包含了从基类继承来的行为,并进行了扩充。
介绍:
类是对现实中事物的抽象,类的继承和派生的层次结构则是对自然界中事物分类、分析的过程在程序设计中的体现。下图说明了某个公司雇员的派生关系。位于最高层的雇员其抽象程度最高,是最具一般性的概念。最下层抽象程度最低,最具体。从上层到下层是具体化的过程,从下层到上层是抽象话的过程。面向对象设计中上层与下层是基类与派生类的关系。
依照面向对象的理念:当某个概念用语言的基本类型不能具体表示时,就应当定义一个新的类型(class)。一个概念一般不是独立存在的,通常与其相关的概念共存,以此发挥更大的作用。当某个概念与其他概念之间存在关系时,应当在相应的类型之间也表示出这样的关系。
类之间的关系主要有以下两种:
(1)“has a”:A car has wheels, engines, …
(2)“is a”:A Manager is an Employee.
‘“has a”的表示方式-聚集(Integration)
一个车有四个轱辘一个引擎。
这里的关系实质上是聚集中的组合。整体拥有各部分,部分与整体共存,如果整体不存在了,部分也会消失,这称之为组合。
class Car
{
Wheel ws[];
Engine e[];
// ...
};
孤立的类只能描述实体集合的特征同一性,而客观世界中实体集合的划分通常还要考虑实体特征方面有关联的相似性。
“相似”无非是既有共同点,又有差别:
(1)内涵的相似性:在客观世界中具有一般——特殊的关系(is a)。例如:雇员(Employee)—经理(Manager)。
(2)结构的相似性:具有相似的表示。例如:array、vector。
那如何表示相似的事物?
如果将相似的事物用不同的类型来表示,能够表示其差别,但体现不了它们之间存在共性的事实,且共性的表示也可能不一致。当扩充维护过程中需要对其共性部分进行修改时,就面临着保持一致性的问题。
如果将相似的事物用相同的类型来表示,则体现其差别就十分困难,且失去了类型化的支持。一旦需要扩充和修改,将影响用此类型表示的所有事物。
方式1、重复定义共性来表示“is a”
方式2、-将共性表示为一种类型来表示“is a”
方式3、通过继承来表示“is a”
上面两张方式无论是那种方式:对于人来讲,虽然能表示出类之间的共性部分 – 但都无法告诉编译器或是其它工具Manager is also an Employee.
由于用了两个(没有指明存在共性关系的)类型来表示:A Manager* is not an Employee*, 所以在使用其中一个的地方就不能使另外一个,特别是,如果不写一些代码不能将 Manager放入Employee的列表(list)中。
正确的方法是通过附加一点信息,显示地声明 Manager is an Employee:
The Manager is derived(派生) from Employee, and conversely, Employee is a base class(基类) for Manager.
除自己的成员以外,类 Manager 还拥有 Employee类的成员。一个派生类被认为是从基类继承了基类的属性,所以这种关系被称为继承Inheritance。
因为基类的成员被派生类所继承,所以继承有益于代码重用(code reuse)。
“has a”关系即组合关系,通过定义类的属性的方式实现的;“is a”关系即继承关系,通过类继承实现。
如果你确定两件对象之间是is-a的关系,那么此时你应该使用继承。比如菱形、圆形、正方形都是形状的一种,那么它们应该服从形状的继承而不是组合。
在实际应用中还会有“has a”和“is a”两者同时用的情况,一个类在接口上表现为“is a”的继承关系,但在实现上又表现为“has a”的组合关系。
继承允许设计者在一个共同的基类中表达多个类的组合特征,例如交通工具就可以作为自行车、汽车等多个类的共同基类。
一个派生类不仅可以从一个基类派生,也可以从多个基类中派生。一个派生类有两个或多个基类的称为多个基类能够多重继承(multiple inheritance)。
关于基类和派生类的关系,可以表述为:派生类是基类的具体化,而基类则是派生类的抽象。
使用继承必须满足的逻辑关系:
如果写了一个基类 A,又写了基类A的派生类B, 那么要注意,“一个B对象也是一个A对象”这个命题从逻辑上成立,是A派生出B 为合理派生的必要条件。
使用继承容易犯的错误:如果写了一个CMan类代表男人,后来又发现需要一个CWoman类来代表女人,仅仅因为CWoman类和CMan类有共同之处,就让CWoman类从CMan类派生而来,是不合理的。因为“一个女人也是一个男人”从逻辑上不成立。
基本概念与语法:
class <类名>:<继承方式><基类名1>, <继承方式><基类名2>,...
{ ... }
构造一个派生类包括以下3部分工作 :
(1) 从基类接收成员。派生类把基类全部的成员(不包括构造函数和析构函数)接收过来,也就是说是没有选择的,不能选择接收其中一部分成员,而舍弃另一部分成员。
(2) 处理从基类接收的成员。接收基类成员是程序人员不能选择的,但是程序人员可以对这些成员作某些处理。
(3) 在声明派生类时增加的成员。这部分内容是很重要的,它体现了派生类对基类功能的扩展。要根据需要仔细考虑应当增加哪些成员,精心设计。
在设计时要根据派生类的需求慎重选择基类,使冗余量最小。
#include <iostream>
using namespace std;
class A
{
public:
int a;
int b;
private:
int c;
protected:
int d;
};
class B: public A
{
int c;
};
main( )
{
cout << ” size of A is” << sizeof(A);
cout << ” size of B is” << sizeof(B);
}
输出:
size of A is 16
size of A is 20
派生类的存储结构与基类的存储结构存在着“粘接(splice)”关系:
sizeof( Base Class ) <= sizeof ( Derived Class )
只有公用派生类才是基类真正的子类型,它完整地继承了基类的功能。基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其派生类对象代替。赋值兼容存在以下三种形式:
(1)派生类对象可以向基类对象赋值;
(2)派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化;
(3)派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。
赋值兼容仅是针对public派生,如果派生方式是private或protected,则上述三条不可行。
可以用子类(即公用派生类)对象对其基类对象赋值,如:
A a;
B b;
A = b;
如已定义了基类A对象a,可以定义a的引用变量:
A a;
B b;
A& r=b;
当通过基类指针或引用操作时,派生类的对象可被当做基类的对象看待。
class Employee
{
public:
string name;
short department;
// ...
};
class Manager : public Employee
{
short level;
// ...
};
void f( Employee* pe ) {};
void main( )
{
Employee e;
Manager m;
f( &e );
f( &m );
Employee *pe = &m;//基类对象指针指向派生类,派生类对象被当做基类对象看待了
pe -> department = ; // OK
pe -> level = ; // error C2039: “level”: 不是“Employee”的成员
}
C++ 继承和派生介绍的更多相关文章
- 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)
[源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...
- O-c中类的继承与派生的概念
什么是继承 众所周知,面向对象的编程语言具有: 抽象性, 封装性, 继承性, 以及多态性 的特征. 那么什么是继承呢? 传统意义上是指从父辈那里获得父辈留下的东西 在开发中, 继承就是"复用 ...
- C++学习之路—继承与派生(三):多重继承与虚基类
(根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 多重继承是指一个派生类有两个或多个基类.例如,有 ...
- C++学习之路—继承与派生(一):基本概念与基类成员的访问属性
(本文根据<c++程序设计>(谭浩强)总结而成,整理者:华科小涛@http://www.cnblogs.com/hust-ghtao,转载请注明) 1 基本思想与概念 在传统的程序设计 ...
- C++复习:继承与派生
1继承概念 面向对象程序设计有4个主要特点:抽象.封装.继承和多态性.说了类和对象,了解了面向对象程序设计的两个重要特征一数据抽象与封装,已经能够设计出基于对象的程序,这是面向对象程序设计的基础. 要 ...
- python的类继承与派生
一.继承和派生简介: 其实是一个一个事物站在不同角度去看,说白了就是基于一个或几个类定义一个新的类.比如定义了动物类接着派生出了人类,你也可以说人类继承了动物类.一个意思.此外python类似于C和C ...
- 模块的封装之C语言类的继承和派生
[交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我 ...
- python3 之 面向对象(类)、继承、派生和多态
类提供了一种 组合数据和功能 的方法.创建一个新类意味着:创建一个新 类型 的对象,从而允许创建一个该类型的新 实例. 每个类的实例可以拥有: 保存自己状态的属性. 一个类的实例也可以有改变自己状态 ...
- c++学习--继承与派生
继承和派生 1 含有对象成员(子对象)的派生类的构造函数,定义派生类对象成员时,构造函数的执行顺序如下: 1 调用基类的构造函数,对基类数据成员初始化: 2 调用对象成员的构造函数,对对象成员的数据成 ...
随机推荐
- 内网DHCP攻击
局域网内DHCP攻击 实验环境:两个win2008 r2虚拟机(一台用作正常的DHCP服务器,另一台用作伪造DHCP服务器),两个win7虚拟机(用作客户机),一个kali虚拟机(用作攻击,耗尽DHC ...
- python第三十课--异常(异常处理定义格式和常见类型)
演示: 1).异常处理的定义格式: 2).常见的运行时异常类型: try: print(10/0) num=int('132a') except Exception as e: print('出错了. ...
- A “Type Erasure” Pattern that Works in Swift:类型域的转换
新视角:通过函数式编程的范畴理论来看待这个问题会更容易理解: 在低层类型无法很好表达的类型,可以将其转化为高阶类型进行表示. 将协议的实现类型转化为monad类型: 解决将具有关联类型的协议当作类型的 ...
- GoldenGate配置(三)之DDL复制配置
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/huangyanlong/article/details/33430293 GoldenGate配置( ...
- pstools工具使用
该工具的目的:批量远程操作windows服务器, 个人实验的方法步骤: 1.在被远程的电脑上开通139,445端口 2.建立ipc$链接, 格式:Net use \\目标ip\ipc$ 密码 /use ...
- C#实现的协同过滤算法
using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace SlopeOn ...
- android studio 导入module作为lib使用
1.将 android module导入 android project 中 2.在要作为lib导入的module 的build.gradle文件中添加一行 “apply plugin: ‘andr ...
- 写出js内存泄漏的问题?
回答一: (1)IE7/8 DOM对象或者Active对象循环引用导致内存泄漏 a.多个对象循环引用 b.循环的DOM泄漏 (2)基础的DOM泄漏 当原有的DOM被移除时,子节点引用没有被移除则无法回 ...
- 连接远程数据库ORACLE11g,错误百出!
客户机中PLSQL DEV访问虚拟机中的ORACLE11g,错误百出! 创建时间: 2017/10/14 18:44 作者: CNSIMO 标签: ORACLE 忙了一下午,只有两个字形容:麻烦! ...
- 接收键盘输入的字符串,用FileWirter类将字符串写入文件,用FileReader类读出文件内容显示在屏幕上
public class SY63 { public static void main(String[] args) throws Exception { System.out.print(" ...