C++ 基础 4:继承和派生
1 继承和派生
在 C++ 中 可重用性是通过继承这一机制实现的。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。
当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定 新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
继承与派生,是同一种意义两种说法。继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物。
如:B 类继承 A 类,可称从类 A 派生类 B。类 A 称为基类(父类),类 B 称为派生类(子类)。
2 派生类的组成
派生类的成员,包含两大部分,一类是从基类继承过来的,一类是自己新增的成员。从基类继承过来的成员表现其共性,而新增的成员体现了其个性,派生类有了自己的个性,使派生类有了意义。
注意:
一个派生类继承了所有的基类方法,但下列情况除外:
基类的构造函数、析构函数和拷贝构造函数。
基类的重载运算符。
基类的友元函数。
3 继承类型
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。
公有继承(public):当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可访问。即基类的公有成员和保护成员被继承到派生类中仍作为派生类的公有成员和保护成员。派生类的其他成员可以直接访问它们。无论派生类的成员还是派生类的对象都无法访问基类的私有成员。
保护继承(protected): 保护继承中,基类的公有成员和私有成员都以保护成员的身份出现在派生类中,而基类的私有成员不可访问。派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但是类外部通过派生类的对象无法访问它们,无论派生类的成员还是派生类的对象,都无法访问基类的私有成员。
私有继承(private):当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可访问。基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类外部通过派生类的对象无法访问。无论是派生类的成员还是通过派生类的对象,都无法访问从基类继承的私有成员。通过多次私有继承后,对于基类的成员都会成为不可访问。因此私有继承比较少用。
注意:
1. 无论何种方式继承基类,派生类都不能直接使用基类的私有成员。
2. 继承时,如果未使用访问修饰符,则继承类型默认为 private。
4 单继承
一个类可以派生自多个类,如果只派生一个,即为单继承。语法如下:
class 派生类名:继承类型 基类名
{
派生类类体;
};
继承类型即是访问修饰符,是 public、protected 或 private 其中的一个。如果未使用访问修饰符,则默认为 private。
假设有一个基类 Shape,Rectangle 是它的派生类,如下所示:
#include <iostream>
using namespace std;
// 基类
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Total area: 35
5 多继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。语法如下:
class 派生类名:继承类型1 基类名1,继承类型2 基类名2,...
{
派生类类体;
};
继承类型即是访问修饰符,是 public、protected 或 private 其中的一个。如果未使用访问修饰符,则默认为 private。
各个基类之间用逗号分隔。
假设有一个派生类 Rectangle 继承于 基类 Shape 与 基类 PaintCost,如下所示:
#include <iostream>
using namespace std;
// 基类 Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 基类 PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// 派生类
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 输出对象的面积
cout << "Total area: " << Rect.getArea() << endl;
// 输出总花费
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
上面的代码被编译和执行时,它会产生下列结果:
Total area: 35
Total paint cost: $2450
6 虚继承
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
6.1 多继承中二义性问题
6.1.1 多重派生类 C 的对象的存储结构示意
6.2 虚继承 virtual
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象。
要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类称为虚基类。
虚继承声明使用关键字 virtual
语法如下:
class 类名: virtual 继承类型 父类名
继承类型即是访问修饰符,是 public、protected 或 private 其中的一个。如果未使用访问修饰符,则默认为 private。
6.2.1 带有虚基类的多重派生类 C 的对象的存储结构示意
7 继承中的构造和析构
7.1 类型兼容原则
类型兼容规则是指 在需要基类的任何地方,都可以使用公有(pulic)派生类的对象来替代。
类型兼容规则中所指的替代包括以下情况:
子类对象可以当做父类对象使用
子类对象可以直接赋值给父类对象
子类对象可以直接初始化父类对象
父类指针可以直接指向子类对象
父类引用可以直接引用子类对象
在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。
示例代码如下所示:
// inheritTest1.cpp,
// 用于验证类型兼容规则
/*
类型兼容规则中所指的替代包括以下情况:
1. 子类对象可以当做父类对象使用
2. 子类对象可以直接赋值给父类对象
3. 子类对象可以直接初始化父类对象
4. 父类指针可以直接指向子类对象
5. 父类引用可以直接引用子类对象
*/
#include <iostream>
using namespace std;
class Parent
{
public:
void printParent()
{
cout << "this is ParentClass" << endl;
}
int parent_age;
};
class Child : public Parent
{
public:
void printChild()
{
cout << "this is ChildClass" << endl;
}
};
void printByPoint(Parent *parent)
{
parent->printParent();
}
void printByReference(Parent& parent)
{
parent.printParent();
}
int main()
{
Child childTest_1;
childTest_1.printParent(); // 1. 子类对象可以当做父类对象使用
cout << "验证 子类对象可以当做父类对象使用 成功" << endl;
Child childTest_2;
childTest_2.parent_age = 18; // 2. 子类对象可以直接赋值给父类对象
cout << "验证 子类对象可以直接赋值给父类对象 成功" << endl;
Child childTest_3;
childTest_3.parent_age = 1; // 注意:子类对象初始化父类对象,必须对父类对象的公有成员也初始化,否则去掉这句会报错
Parent parentTest_3 = childTest_3; // 3. 子类对象可以直接初始化父类对象
cout << "验证 子类对象可以直接初始化父类对象 成功" << endl;
Child childTest_4;
Parent *parentTest_4 = NULL;
parentTest_4 = &childTest_4; // 4. 父类指针可以直接指向子类对象
printByPoint(parentTest_4);
cout << "验证 父类指针可以直接指向子类对象 成功" << endl;
Child childTest_5;
Parent &parentTest_5 =childTest_5; // 5. 父类引用可以直接引用子类对象
printByReference(parentTest_5);
cout << "验证 父类引用可以直接引用子类对象 成功" << endl;
return 0;
}
运行结果:
7.2 继承中构造析构调用原则
- 子类对象在创建时会首先调用父类的构造函数,父类构造函数执行结束后,执行子类的构造函数
2.当父类的构造函数有参数时,需要在子类的初始化列表中显式调用
3.析构函数调用的先后顺序与构造函数相反
示例代码如下所示:
// inheritTest2.cpp,继承中构造析构调用原则
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(const char* temp_s)
{
this->s = temp_s;
cout << "父类有参构造函数运行"<< endl;
}
~Parent()
{
cout << "父类析构函数运行" << endl;
}
private:
const char* s;
};
class Child : public Parent
{
public:
Child(int temp_age):Parent("我是父类")
{
this->age = temp_age;
cout << "子类有参构造函数运行"<< endl;
}
Child(int temp_age,const char *temp_s):Parent(temp_s)
{
this->age = temp_age;
cout << "子类有参构造函数运行"<< endl;
}
~Child()
{
cout << "子类析构函数运行" << endl;
}
private:
int age;
};
int main()
{
Child child_1(18);
//Child child_2(18,"我是父类"); //这条语句也可实现
return 0;
}
运行结果:
C++ 基础 4:继承和派生的更多相关文章
- python 之 面向对象基础(继承与派生,经典类与新式类)
7.2 继承与派生 7.21继承 1.什么是继承? 继承是一种新建类的的方式,在python中支持一个子类继承多个父类.新建的类称为子类或者派生类,父类又可以称为基类或者超类,子类会”遗传“父类的属性 ...
- Python基础之继承与派生
一.什么是继承: 继承是一种创建新的类的方式,新建的类可以继承一个或过个父类,原始类成为基类或超类,新建的类则称为派生类 或子类. 其中,继承又分为:单继承和多继承. class parent_cla ...
- python基础——继承与派生、组合
python基础--继承与派生 1 什么是继承: 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类成为基类或超累,新建的类成为派生类或子类 1.1 继承分为:单 ...
- Python开发基础-Day18继承派生、组合、接口和抽象类
类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...
- python基础之继承派生、组合、接口和抽象类
类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...
- C++基础之继承类和派生类
(1)继承是创建一个具有某个类的属性和行为的新类的能力.原有的类称为基类,新创建的类称为派生类.派生类将基类中的所有成员作为自己的成员,同时派生类本身可以定义新的成员(2)派生类只有一个基类的继承称单 ...
- 四.OC基础--1.文档安装和方法重载,2.self和super&static,3.继承和派生,4.实例变量修饰符 ,5.私有变量&私有方法,6.description方法
四.OC基础--1.文档安装和方法重载, 1. 在线安装 xcode-> 系统偏好设置->DownLoads->Doucument->下载 2. 离线安装 百度xcode文档 ...
- C/C++基础知识总结——继承与派生
1. 类的继承与派生 1.1 派生类的定义 (1) 定义规范 class 派生类名: 继承方式 基类1名, 继承方式 基类2名... { ...派生类成员声明; }; (2) 从以上形式上看可以多继承 ...
- python基础之类与对象,继承与派生
类与对象 对象的本质也就是一个名称空间而已,用于存放自己独有的属性,而类中存放的是对象共有的属性. __init__会在调用类时自动触发 调用类时发生两件事: 1.创建一个空对象stu1 2.自动触发 ...
- Python基础(16)_面向对象程序设计(类、继承、派生、组合、接口)
一.面向过程程序设计与面向对象程序设计 面向过程的程序设计:核心是过程,过程就解决问题的步骤,基于该思想设计程序就像是在设计一条流水线,是一种机械式的思维方式 优点:复杂的问题的简单化,流程化 缺点: ...
随机推荐
- SpringBoot logback 配置文件自定义属性
添加自定义属性类 package com.cus.config; import ch.qos.logback.core.PropertyDefinerBase; import org.springfr ...
- postgresql 和 mysql 数据库备份恢复以及时区问题
概要 postgesql 12 备份/恢复脚本 时区设置 mysql 5.6 备份/恢复脚本 时区设置 概要 postgresql 和 mysql 是最常用的 2 种开源关系数据库, 很多项目也会优先 ...
- 发布MeteoInfo 1.2.4
在JLaTeXMath库(http://forge.scilab.org/index.php/p/jlatexmath/)的支持下,实现了利用LaTeX语法显示特殊符号和数学公式的功能.需要在字符串首 ...
- es6深层次数组深拷贝
let arr = [ { label: '1', children: [1, 2] } ] let a = [{...arr[0]}] ...
- 【动态规划】DP搬运工3
UPD:修了点锅(啊昨天居然写脑抽了) 题目内容 给定两个长度为 \(n\) 的序列,定义 \(magic(A,B)=\sum\limits_{i=1}^n \max(A_i,B_i)\). 现在给定 ...
- js实现自定义弹窗
众所周知,浏览器自带的原生弹窗很不美观,而且功能比较单一,绝大部分时候我们都会按照设计图自定义弹窗或者直接使用注入layer的弹窗等等.前段时间在慕课网上看到了一个自定义弹窗的实现,自己顺便就学习尝试 ...
- beego log
package main import ( "github.com/astaxie/beego/logs" _ "xcms/routers" _ "x ...
- centos8安装zookeeper(单机方式)
一,下载zookeeper: 1,官网地址 http://zookeeper.apache.org/ 找到这个地址: https://mirrors.tuna.tsinghua.edu.cn/apac ...
- centos8上配置openresty/nginx可访问php
一,创建一个测试站的目录 [root@yjweb data]# mkdir dev [root@yjweb data]# cd dev [root@yjweb dev]# mkdir think_ww ...
- Angular双向绑定简单理解
在使用Antd的时候,一直很好奇里面的双向绑定的自定义组件是怎么做的. 因为之前一直用,没有去细看文档. 今天抽空来简单的撸一下. 在ng中,()是单向数据流,从视图目标到数据源,[()]这样就是双向 ...