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)派生类的对象来替代。

类型兼容规则中所指的替代包括以下情况:

  1. 子类对象可以当做父类对象使用

  2. 子类对象可以直接赋值给父类对象

  3. 子类对象可以直接初始化父类对象

  4. 父类指针可以直接指向子类对象

  5. 父类引用可以直接引用子类对象

在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。

示例代码如下所示:

// 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 继承中构造析构调用原则

  1. 子类对象在创建时会首先调用父类的构造函数,父类构造函数执行结束后,执行子类的构造函数

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:继承和派生的更多相关文章

  1. python 之 面向对象基础(继承与派生,经典类与新式类)

    7.2 继承与派生 7.21继承 1.什么是继承? 继承是一种新建类的的方式,在python中支持一个子类继承多个父类.新建的类称为子类或者派生类,父类又可以称为基类或者超类,子类会”遗传“父类的属性 ...

  2. Python基础之继承与派生

    一.什么是继承: 继承是一种创建新的类的方式,新建的类可以继承一个或过个父类,原始类成为基类或超类,新建的类则称为派生类 或子类. 其中,继承又分为:单继承和多继承. class parent_cla ...

  3. python基础——继承与派生、组合

    python基础--继承与派生 1 什么是继承: 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类成为基类或超累,新建的类成为派生类或子类 1.1 继承分为:单 ...

  4. Python开发基础-Day18继承派生、组合、接口和抽象类

    类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...

  5. python基础之继承派生、组合、接口和抽象类

    类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...

  6. C++基础之继承类和派生类

    (1)继承是创建一个具有某个类的属性和行为的新类的能力.原有的类称为基类,新创建的类称为派生类.派生类将基类中的所有成员作为自己的成员,同时派生类本身可以定义新的成员(2)派生类只有一个基类的继承称单 ...

  7. 四.OC基础--1.文档安装和方法重载,2.self和super&static,3.继承和派生,4.实例变量修饰符 ,5.私有变量&私有方法,6.description方法

    四.OC基础--1.文档安装和方法重载, 1. 在线安装 xcode-> 系统偏好设置->DownLoads->Doucument->下载 2. 离线安装 百度xcode文档 ...

  8. C/C++基础知识总结——继承与派生

    1. 类的继承与派生 1.1 派生类的定义 (1) 定义规范 class 派生类名: 继承方式 基类1名, 继承方式 基类2名... { ...派生类成员声明; }; (2) 从以上形式上看可以多继承 ...

  9. python基础之类与对象,继承与派生

    类与对象 对象的本质也就是一个名称空间而已,用于存放自己独有的属性,而类中存放的是对象共有的属性. __init__会在调用类时自动触发 调用类时发生两件事: 1.创建一个空对象stu1 2.自动触发 ...

  10. Python基础(16)_面向对象程序设计(类、继承、派生、组合、接口)

    一.面向过程程序设计与面向对象程序设计 面向过程的程序设计:核心是过程,过程就解决问题的步骤,基于该思想设计程序就像是在设计一条流水线,是一种机械式的思维方式 优点:复杂的问题的简单化,流程化 缺点: ...

随机推荐

  1. Java变量命名前俩个字母仅含有一个大写字母的坑

    背景 前几周在做项目fetch切换,即将HttpUtils调用改成使用Feign调用.大概代码如下: // 原代码 String resultJson = HttpUtil.get(url + &qu ...

  2. abp(net core)+easyui+efcore实现仓储管理系统——出库管理之二(五十)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...

  3. 如何获取value值,获取属性值,设置属性值,

    1.获取select下拉框的value值,   2.获取select  的tid值 3.设置属性值  4.判断哪个单选框选中了 prop好像是判断的意思吧,个人理解勿喷谢谢!!!!!!

  4. SpringBoot 完整学习笔记免费分享

    从0到进阶,完全系统性的学习笔记 每次我都会反复拿来观看,因为我们总会有遗漏忘记的地方,但是笔记不会. 希望大家能好好利用它,以下是笔记截图! 以上只是其中的一项部分,这份笔记可以说含金量超高,绝对会 ...

  5. Create a cursor from hardcoded array instead of DB

    https://stackoverflow.com/questions/18290864/create-a-cursor-from-hardcoded-array-instead-of-db Crea ...

  6. hasura的golang反向代理

    概述 反向代理代码 对请求的处理 对返回值的处理 遇到的问题 概述 一直在寻找一个好用的 graphql 服务, 之前使用比较多的是 prisma, 但是 prisma1 很久不再维护了, 而 pri ...

  7. 多测师讲解selenium _滚动条定位_高级讲师肖sir

    from selenium import webdriverfrom time import sleepdrvier=webdriver.Chrome()drvier.get('http://www. ...

  8. 多测师讲解selenium_alert弹框定位_高级讲师肖sir

    from selenium import webdriverfrom time import sleepdrvier=webdriver.Chrome()url=r'F:\dcs\DCS课程安排\se ...

  9. C++分隔字符串split

    split C++标准库中没有提供split分隔字符串的函数,哎. 实现一 下面的实现需要指定分隔符的集合delimiters,以及是否将连续的分隔符看作同一个分隔compress : enum cl ...

  10. pytest文档51-内置fixture之cache使用

    前言 pytest 运行完用例之后会生成一个 .pytest_cache 的缓存文件夹,用于记录用例的ids和上一次失败的用例. 方便我们在运行用例的时候加上--lf 和 --ff 参数,快速运行上一 ...