C++学习摘要

文章详细介绍了C++编程的基础知识,包括C++与C的区别,如命名空间、输入输出流、引用与指针、函数重载、动态内存分配。深入探讨了面向对象编程的核心概念,如类、对象、构造函数、析构函数、拷贝构造函数、静态成员、友元、继承和多态。此外,还讲解了运算符重载、模板的使用,以及STL中的list和map容器。文章通过实例展示了如何在C++中实现这些概念和技术。
摘要由CSDN通过智能技术生成

Day1

    • C++与C的区别

各自是什么?特点?不同地方?

<1>字面上:第一个词是一样,C++是完全兼容C,只要是合法的C的程序在C++中都是合法的!C++是C的增强版本

<2>. C++比C多了两个+,C++中扩展了很多内容,数据类型:引用、类类型、布尔类型等!关键字:friend、class、bool、public等!

<3>.编程思想是不一样的,C语言是面向过程的结构化的程序设计语言,中级语言。

C++是面向对象的编程语言,更接近自然界的发展规律!

什么是面向过程

注重的是过程,只有过程对了,才有可能得到正确的结果;

C语言中其实也有面向对象,只不过不够彻底;

什么是面向对象:

注重的是结果

学生{静态的属性和行为整合在一起};

例子:红烧鱼: 自己做—按步骤一步一步的去实现

点外卖:不关注店家是怎么做的,我只要结果

C++应用场景:

  1. QT:嵌入式产品的图形界面UI设计- C++实现的

  1. 人工智能:图像处理—opencv框架 C++去实现

  1. 游戏方向

C++中的不同于C的特性

    • 命名空间

在C语言中,同一个作用域中能不能出现同名的变量?出现同名的函数?自定义同名的数据类型? 答案是不可以的!

在实际生活中,一定会出现同名的!例如:一个班级中有同名的学生!一个校区的不同班级中也可以有同名的学生!

是什么? 作用范围

解决问题:解决命名冲突的问题!变量、函数、自定义的数据类型

什么时候用?

一个老师带A班,B班;A班有张三,B班也有张三;

老师叫张三的时候,有可能来的是A班的,也有可能来的是B班,如何区分?

叫人的时候加上归属(作用域): A的张三

怎么用:

命名空间格式

namespace 命名空间的名字{可以有变量,也可以有函数,还可以有自定义的数据类型};

使用:

命名空间名字::变量等!

使用using namespace 命名空间名字;切换到对应的范围中,直接使用即可!

    • 标准输入输出流

C语言中标准的输入输出头文件 stdio.h

库函数Scanf、printf在C++中是可以直接使用的!

除此之外,C++中也有提供专门用来做输入输出流!

什么是流: 以字节为单位,连续不断,方向一致的称为流!

输入流对象: cin

使用:cin>> 变量;

输出流对象:cout

使用:<< 变量;

好处:输入输出不需要做格式的限制,使用简单

代码实现:

    • 引用类型-----引用与指针的区别(面试常问的题目)

什么是指针:保存地址,得到地址,可以对地址中的内容做修改、访问!

应用在:函数参数设计

引用: 可以实现和指针一样的效果。

目的:

  1. 弱化指针,能用指针实现的可以替换成引用!

  1. 减少临时空间的分配:int *p;就需要给p分配内存空间;引用是不需要分配内存空间。

什么是引用: 就是给变量起别名,变量是已经存在的,别名和变量指的是同一块内存空间!

定义的格式: 数据类型&引用名= 变量名;

Int p = 10;

Int &pt = p; // 这里的pt就是给p起的别名

应用:在函数参数设计上

案例:两数交换

整理一下:引用与指针的区别:

  1. 引用是给变量起别名;指针是用来保存地址;

  1. 定义指针变量系统会给分配内存空间,定义引用不会额外的分配内存空间;

  1. 指针变量定义完之后可以不进行初始化,但是一般为了避免野指针,会初始化为NULL;

引用定义完必须进行初始化

  1. 指针变量是可以改变指向,但是引用一旦初始化之后不可以改变指向。使用引用比使用指针安全!

  1. 应用:都可以设计成函数的参数类型。

说明一个常量的方法:

使用宏定义

使用const修改引用---通常应用在函数参数设计上!

    • 函数重载

函数名的设计比较困难

两数求和或者交换封装函数:

Int sum(int a, int b); //只能实现两个int类型数的求和

Double sum_double(double a, double b);

char sum_char(char a, char b);

解决问题:函数功能相似,参数不同(参数类型不同)的一组函数,解决函数名命名困难的问题!(在C语言中不能出现同名函数)

函数重载定义:函数名相同,函数功能要相似,参数不同(参数类型不同、参数个数不同、参数类型的顺序不同),与返回值类型无关的一组函数!

函数调用的原理:

函数定义完之后在内存中是有一块空间,编译成2进制文件,调用函数的地方存的就是函数在内存中的地址;根据地址找到对应的代码块去执行(函数名指的就是函数的地址),执行完之后再反回到调用的地方继续往下执行!

验证为什么在C中不可以有同名函数,而C++中允许有

使用gcc和g++分别去编译了同一个.c源文件,观察了可执行二进制文件,发现:

使用gcc编译完之后,函数名没有发生变化

使用g++去编译的时候,函数名发生变化了,函数名字根据函数参数类型以及个数进行重命名!

函数名不同,函数在内存中的地址就不同!

总结

函数重载的原理:使用g++进行编译的时候,函数名会根据参数类型以及参数个数进行重命名,函数名字不一样,函数在内存中的地址就不同,调用的时候执行的就是不同的函数体!

    • 参数默认值

什么是参数默认值:就是在函数定义的时候给了形参一个初始值。

当我们在调用函数的时候,可以选择性的给这个参数值,可以给它值也可以不给它值!如果不给它值,按默认值参与函数体中的运算;如果给了它值,就按给的值参与函数体中的运算!

当函数参数有默认值,可以解决函数重载中只有参数个数不同的而造成的代码冗余!

在设计函数参数默认值的时候要注意:

1.参数默认值给的顺序是从右向左的;调用函数的时候参数匹配的顺序是从左向右的!

2.在函数重载中,当函数参数有默认值的时候要注意二义性,不然就会出错!

解决方案:

3.当函数声明和函数定义同时存在的时候,参数的默认值只能出现一次,建议放在函数声明的地方!

    • 如何在C++中引入C的库

C中的标准库在C++中使用:直接#include<>即可!

C中自定义的库如何在C++中使用呢?

gcc :编译完之后函数名不变

g++: 编译完之后函数名变化

验证:

制作C的自定义的库 .h .c 生成静态库

如何解决:

目标:C的库既能在C中使用,也能在C++中使用!

先要明确当下使用的编译器是g++还是gcc?

如何判读编译器是g++还是gcc?

方法1:

方法2:计算一个字符占的内存大小,也可以来区分!!!

最终的解决方案:

Extern “C”{} 告诉C++编译器按照C的规则去处理即可

验证ok!

8、动态内存分配

C中的动态内存分配:

malloc库函数

free库函数

C++中有提供另外的动态内存分配的方法:

New运算符 –开空间

Delete运算符—释放空间

书写格式:

数据类型*指针变量 = new 数据类型;==》 delete 指针变量;

数据类型*指针变量 = new 数据类型[个数];==》 delete [] 指针变量;

整理面试题目: new和malloc的区别?

  1. New是C++中的运算符,malloc是C中标准的库函数;

  1. 书写:new后面只要跟数据类型即可,并且开空间后不需要进行强制类型转换;

Malloc开空间:要给开空间的大小,并且开空间后要进行强制类型转换;

  1. New开的空间用delete去释放; malloc开的空间free去做释放;

  1. New开的空间可以让做初始化,malloc开的空间初始化要自己用memset去做;

  1. 对于类类型来说,new的方式去创建对象,会自动的调用构造函数;delete释放对象的时候会自动调用析构函数;

Malloc是不会执行构造函数的,free是不会执行析构函数的!!!

面试题目:

为什么有了malloc和free还需要new和delete?

作业:

  1. 整理笔记,用思维导图,记得截图发班级群

  1. 完成课堂练习

  1. 代码题:实现两数交换的重载,尽量多写

  1. 总结指针与引用的区别? New和malloc的区别?

Day2

    • 面向对象编程中的类和对象

面向过程编程:一步一步的去实现;

面向对象编程:点外卖,坐等着吃

在整个程序都是由一个一个的对象组成

对象是由数据结构+算法组成的,这个其实就是通过面向过程实现的!

C里面面向对象:是由结构体来实现的!

链表==》就是一个一个的结构体组成;除了设计了结构体,还有封装了一些函数(对链表进行增删改查的操作)。

什么是结构体:为了描述一类事物的属性而自定义的一种数据类型!

学生:

属性:学号、姓名、性别、出生日期、成绩。我们结构体只能描述属性,并不能把学生描述的活灵活现的,缺少行为。

行为:上课、吃饭、睡觉、做作业

访问: 变量名.属性名!

    • C++中的结构体与C中结构体的区别?

<1>.c结构体中不能有函数;而C++结构体中是可以有函数的!

<2>.c中空结构体的大小为0字节;C++中空结构体的大小为1字节

C中的结构体面向对象不彻底,C++中面向对象,就是把属性和行为整合在一起来完整的描述一类事物! 可以用结构体,但是他有提供专门的一种数据类型来表示的!

什么是类:为了描述一类事物的属性和行为而自定义的一种数据类型!

把属性和行为整合在一起 {}

类的关键字: class

格式:上

Class 类名{属性;行为};

C++中的结构体:struct 名字{属性;行为};

C++中有提供一个专门用来表示字符串的数据类型— string 类

    • C++中的类与C++中结构体的区别?

<1>.关键字不同:类是用class 结构体用struct

<2>.初始化的方式是不一样的:

结构体的初始化是数据类型变量名= {};

但是类变量的初始化是不能按结构体的方式去做;

<3>.成员默认的权限是不同的

结构体中成员的默认权限是公共的 public

类中成员的默认权限是私有的 private

<4>结构体定义出来的变量就是变量;

类数据类型定义出来的变量叫对象

补充:

C++中类成员的访问限定符有3种

public: 公共的,公有的 类的外部是可以直接访问的

private: 私有的 类的外部是不可以访问的,类的内部是可以访问的!

protected: 受保护的

什么是类的外部:{}外

什么是类的内部:{}内

什么是对象就是用类这个数据类型定义的变量;

类和对象的关系:

类这个数据类型是抽象;对象是具体的。

类是对象的抽象;对象是类的具体实例!

例子:手机 我的手机

定义数据类型的时候,系统是不会给分配内存空间的,但是它是有大小的;只有在定义变量的时候才会按照这个数据类型的大小给变量分配内存空间!!!

面向对象编程有4大特性

抽象、封装、继承、多态

    • 封装---面向对象编程中4大特性

面试题目:谈谈你对封装的理解(谈谈你对面向对象编程的理解)

封装:把一类事物的属性和行为整合在一起,封装!

抽象:把一类事物的共同的属性和行为提取出来。

总结封装

把一类事物的属性和行为提取出来,用自定义的数据类型 包在一起,该隐藏的隐藏起来,该暴露的暴露出来!

哪些是需要隐藏起来的:属性

哪些是需要暴露出来的:行为—接口

用面向对象的方式来描述手机

属性:价格、品牌、颜色、运行内存、内存、像素的等等

行为:开机、关机、打电话、发消息、上网、拍照等

观察:不同的对象调用同一个函数,呈现不同的结果,为什么?

    • This指针

This:是这个的意思,它指的就是类对象的地址。

哪个类对象的地址:谁去调用成员函数,this指针的就是谁的地址!

总结this:

  1. 类的每一个成员函数中都有一个隐藏的参数this,参数的格式:

类名*const this;

  1. 使用的时候,哪个对象来调用成员函数,this保存的就是哪个对象的地址;

  1. 在成员函数的实现过程中,如有访问到类自己的数据成员的时候建议加上this!

避免因为形参名字和数据成员名字相同造成数据混乱!

要访问类的成员,必需通过对象去访问;

普通对象+.成员;

指针类型的对象+->成员;

    • 类中特殊的成员函数--构造函数

本质:是函数;

作用:在创建对象的时候,给对象分配内存空间并对数据成员进行初始化的。

何时调用:在创建对象的时候被调用的

谁调用: 系统自动调用

如果我们自己没有定义构造函数的话,系统会帮我们自动的创建一个空的构造函数;如果我们自己有写,走我们自己定义的构造函数!

定义的格式:

无函数类型 函数名和类名是一样的

函数名(参数列表){}

构造函数是可以重载的!

参数有默认值:

什么是默认构造函数:

<1>.构造函数无参的

一种是我们自己写的;一种是系统帮我们自动生成的

<2>.有参数的构造函数,并且所有参数都有默认值!

注意:在一个类中,默认构造函数只能存在一个!他们是互斥的关系!

如果后期想要修改或者获取类的私有的数据成员时,类外不能访问,就提供相应的成员函数!!!

注意如下的写法::

    • 类中特殊的成员函数--析构函数

本质:函数

作用:在对象生命周期结束的时候,做资源释放回收的!特别是在堆区分配的空间一定要通过free或者delete去做释放!

何时调用:在对象生命周期结束的时候

谁去调用:系统自动去调用

析构函数如果我们自己没有定义,系统会帮我们自动生成一个空的析构函数;如果我们自己写了,会调用我们自己写的析构函数!

定义格式:

无参数类型 无参 函数名和类名是一样,前面要加上~

~类名(){}

析构函数是不能进行重载的!!!!

类对象的赋值,观察:

既然有执行析构,说明一定有执行构造函数!

    • 拷贝构造函数

何时执行拷贝构造函数:

用一个已经存在的对象去初始化一个正在创建的对象的时候才会执行拷贝构造函数!

拷贝构造函数分为两种:

深拷贝 浅拷贝

什么是浅拷贝:

单纯的把一个类对象的数据成员值赋值给另外一个类对象的数据成员

存在的问题:

分析:

当myphone对象在释放的时候,p指向的堆区空间已经被释放了,再次释放p就会造成同一块堆区的空间被释放两次,就出现野指针

如何解决:进行深拷贝

什么是深拷贝:

在拷贝构造函数中对指针类型的数据成员重新new分配空间,再把数据拷贝到堆区空间中!

分析原理:

什么时候进行深拷贝,什么时候进行浅拷贝:

如果构造函数里有在堆区开空间的话,要进行拷贝就需要进行深拷贝。

系统帮我们自动生成的拷贝构造是浅拷贝构造函数;如果要进行深拷贝,拷贝构造函数一定要自己去写!

类对象数组以及类对象指针数组:

作业:

  1. 整理笔记,输出思维导图,单独截图发班级群

  1. 定义一个学生的类,要求要有构造函数、析构函数、拷贝构造函数

并且去创建对象,创建指针类型对象和普通对象;

要能够修改学生的信息,也要能够显示学生的信息!

  1. 以面向对象的方式实现一下登录界面

Day3

    • static修饰类的成员

回顾一下C中的static的作用

<1>static修饰全局变量:限制全局变量的作用域

<2>static修饰局部变量:延长局部变量的生命周期

<3>static修饰函数: 限制了函数的作用域

案例:

学生类 学生学号想让它自增,如何实现?

    • static修饰类的数据成员

初始化:类内声明,类外初始化

类的静态数据成员特点:

  1. 属于类但是不属于某一个对象;所有的对象共用一个变量

  1. 没有对象也可以进行访问

访问:

  1. 类名::变量名;

  1. 对象名.变量名; 指针类型对象名->变量名;

解决问题:

当我们想要实现数据共享的时候,就可以使用类的静态的数据成员!

例如:统计个数、ID自增!

    • static修饰类的成员函数

特点:

  1. 静态的成员函数没有this指针;

  1. 静态的成员函数只能访问静态的成员,不能访问非静态成员

  1. 非静态的成员函数可以访问静态的成员

访问:

类名::成员函数();

对象.成员函数(); 指针对象->成员函数();

应用:共享

案例:

设计一个Ctool类,然后这个类就是用来做共享,说明这个类里面的成员函数都设计成静态!

Class Ctool

{

Static void gotoxy(int x, int y);

Static void paintWindow(int x, int y, int w, int h);

};

使用:类名::gotoxy(10,20);

    • const修饰类的成员

回顾const作用:

Const int b = 10; // b的值不可以修改

const int *a = &b; // *a不可以修改,a的值是可以变化的

int * const a= &b; // a的值不可以修改,但是*a的值是可以变化的

const的右侧是什么,什么不能变!

2-1. const修饰类的数据成员

Const修饰数据成员,数据成员的值是不可以修改的!

如何初始化:

使用初始化列表的方式,在构造函数首部的后面;这个类的所有的构造函数都要用初始化列表对const修饰的数据成员进行初始化

非const修饰的数据成员也可以使用初始化列表进行初始化

应用场景:就是一些类的数据成员值一旦设计好了是不可以修改的,例如身份证ID

2-2、const修饰的成员函数

<1>const修改的成员函数就变成了常函数,本质上修饰的隐藏起来的参数 this!

只能访问成员但是不能修改。

<2>const修饰的成员函数中是不能调用非const修饰的成员函数的!

<3>非const修饰的成员函数中是可以调用const修饰的成员函数的

应用:例如教务系统中我们只能查看成绩,成绩有问题是不能自己去修改!

    • const修饰类的对象

const修饰的类对象(常对象),不能调用非const修饰的成员函数;只能调用const修饰的成员函数!

常对象只能调用常函数!

笔试题目:问程序的输出结果

用const修饰了成员函数,这个函数就不能对数据成员进行修改,如果我在一种特殊的情况下,一定要修改数据成员的值,怎么办?

    • mutable关键字

作用:解决const修饰的成员函数中没法修改数据成员值的问题

使用:就是在数据成员声明的前面加上mutable

尽量少用!!!因为他其实会破坏我们类封装的完整性!除非万不得已!

    • 友元

友元就是朋友的意思!

关键字:friend

什么时候选择用友元:多次频繁的访问或者修改类的数据成员的时候

解决问题:

<1>普通的函数做类的友元

注意:

友元函数不是类的成员函数,所以不受类的访问限定符的限制!

<2> 一个类的成员函数做友元:类中的成员函数要频繁的访问另外一个类的数据成员

  1. 类要做前向声明

  1. 类中的成员函数要类的外部去定义

<3>整个类做朋友:类中的多个成员函数要频繁的访问另外一个类的数据成员,将整个类作为朋友!

存在的问题:

友元会破坏类封装的完整性!没有特殊情况尽量不使用!

    • 案例

作业:

  1. 整理笔记,思维导图

  1. 完善标签、编辑框、按钮类

  1. 完成Ctool类的定义,实现成员函数的共享

  1. 绘制登录界面、注册界面

  1. 验证一下键盘上面方向按键的ASCii值、回车的ASCii值、退格的ASCii值。

Day4

    • 运算符重载

函数重载:函数名相同、函数功能相似、参数不同(个数、类型、顺序)、与返回值类型无关的一组函数!

运算符:+、-、++、--、= 等等

为什么要进行重载的?

两数求和:

类型:int、 double、char、 类对象求和呢?

为什么要进行运算符重载:

让我们的运算更丰富一些,适用各种各样的数据类型!

运算符重载的方式有几种?

两种,一种是使用成员函数进行重载;使用友元函数进行重载。

+:必须是两个

注意: 运算符重载的时候,操作数的个数是不能发生变化的,并且参数是不能有默认值!

格式是什么样子?

友元的格式:

类类型Operator +(参数1, 参数2);

成员的格式:

类类型Operator +(参数2);

以复数类为例子: 1+2j 2+3j

1-1、友元函数重载+运算符

使用:

1-2、成员运算符+重载:

使用

区别:

友元运算符重载的参数个数比成员运算符重载的参数个数多一个;

成员运算符重载:隐藏的参数this代表它自己,表示第一个操作数,只需要给第二个操作数即可!

函数类型是类类型的时候,也会执行拷贝构造函数!

练习:-运算符的重载,截图发班级群!!!!

1-3、前++运算符重载

什么时候函数类型设计成类类型的引用:

如果我们返回的对象和参数传进来的对象是同一个的话,用引用!

1-4、后置++运算符重载

调用:

练习:前置--、后置—运算符重载!

问: 所有的运算符都可以通过友元和成员函数进行重载吗?

1-5、<<运算符重载—友元重载

输出流对象<< 输出的内容

=赋值运算符 通过成员函数进行重载!!! []

    • 模板:函数模板和类模板

什么是模板:

Ppt、简历

泛型编程: 就是类型是不确定的!逻辑是一致的!

2-1、函数模板

两数求和:

函数重载存在的问题:

只要类型不同就需要重新定义函数,造成逻辑冗余!

解决: 把数据类型参数化

共同的逻辑提取出来,得到了函数模板!

函数模板的格式:

template<class/typename 形参>

函数类型 函数名(参数列表){}

函数模板是函数吗?不是函数,是模板!是不占内存的

什么时候函数模板才会变成真这个的函数呢?

实例化的时候

实例化的时候类型一样的,模板函数只会生成一个,后面要用的时候直接使用即可!

函数模板是可以进行重载的!

使用:

2-2、类模板

单链表为例子:

链表是由一个一个的节点组成的。

每一个节点:数据域和指针域

一个类是模板,类中的所有的成员函数都是函数模板!!!

使用:

练习:写两数比大小的函数模板,并测试!!!

    • C++中标准模板库STL

C++中很多代码都是通过模板去实现的!

STL S标准 T模板L 库

就包含了很多的函数模板和类模板

学习一个类模板

3-1、 vector—向量—数组

是一种顺序容器,容器就是用来保存数据

结构

引入文件 #include <vector>

容器的begin() 和end()的位置如下:

容器是什么类型的,迭代器就是什么类型的!

面试题目:

总结一下数组和vector的区别?

STL中的几大组件:

https://blog.csdn.net/NXHYD/article/details/108072498

    • 案例:编辑框对象可以做输入、光标可以上下切换—明天讲

5、作业:

  1. 整理笔记,思维导图

  1. 练习课堂上的代码,运行结果截图放到word文档中,工程提交!!!

<1>运算符重载:-、前--、后—

<2>函数模板:两个数比大小的函数模板,验证

<3>类模板: 链表类模板的实现,验证—保存学生信息

新增、获取节点个数、打印学生信息

<4>向量容器的使用:保存5个学生信息,实现:增、删、改、显示。

  1. 登录类、注册类的实现,界面绘制!

Day5

    • STL中的list容器和map容器

1-1、list容器

底层结构:双向循环链表

使用:

#include <list>

新增:

访问:

删除:

修改:

Splice:移动:

1-2、map容器

底层结构:二叉树 特点:查找速度很快

书写结构:哈希表

Key ---value

Map的特点: 会自动排序,按key值从小到大的顺序排序

Key-value的键值对 根据key可以快速的找到value值,他们是一一对应的关系

新增:

访问:

删除以及修改:

面试题目:

知道STL ?用过里面的什么容器?他们之间的区别是什么?vector、list、map的区别

回答的时候:

1、三个容器各自的结构

2、特点?

3、应用场景:?

1-3、案例: 使用C++map实现电子词典

    • 继承

为什么要使用继承,解决什么问题?

减少代码冗余,新写的代码中可以直接使用已经有了的代码,提高开发效率!

qq版本更新:

其实就是在原有的代码的基础上新增一些功能代码或者是完善一个功能!

面向对象编程:

原有类的基础上创建新的类出来,新的类中可以直接使用原来类中的内容,新的类中可以增加新的数据成员和成员函数!

继承的好处:

减少代码冗余、提高开发效率、减少出错!

什么使用继承:

一个类是另外一个类的特殊版本的时候,就可以使用继承!

父类与子类

父类:已经存在的类

子类:新创建的类

继承是如何继承的呢?

子类继承父类的所有(数据成员和成员函数)友元是否会被继承?不会被继承,因为他不是类的成员,不具有传递性!构造函数和析构函数是不会被继承!

继承的格式是什么样子的?

Class 子类名:继承方式 父类名{};

继承方式有三种:

Public继承:

Private继承:

Protected继承:

人类 ---》老师类

<1>验证了父类的数据成员确实被子类继承了

<2>.验证了父类的成员函数被子类继承

Protected的数据成员在子类中是可以使用!

总结:

  1. 只有public方式继承,父类的成员访问权限和原来是一样的!

  1. 用private、protected方式继承,父类的public成员在子类外都不可访问!

  1. 一般都使用public方式继承!

  1. 父类的protected成员在子类内可以访问!

    • 继承关系中子类的构造函数

在继承关系中,创建父类对象,只会执行父类的构造函数!

在继承关系中,创建子类对象,会先执行父类的构造函数,再执行子类的构造函数;

创建父类与子类对象的时候,在内存中是什么样子的?

    • 继承关系中子类的析构函数

先构造的后析构

题目:看程序写运行结果!!!!

    • 多层继承

  1. 》B—》C—》D

人类—》工人类—》教师类

创建子类对象,构造的执行顺序是先执行最上层的父类的构造函数,依次往下执行,最后走才是自己的构造函数!

当我们子类中有和父类同名的成员函数存在的时候,父类同名的成员函数被隐藏,如果通过子类对象直接去调用的话,执行的是子类的成员函数;

要想让执行父类的同名函数:要指定归属!

为什么要在子类中定义和父类同名的成员函数呢?

就是因为要实现同样的功能,子类的做法和父类的做法是不同的!

重定义(隐藏):子类定义了和父类同名的函数!因为从父类继承下来的方法实现的效果满足不了子类的需求!

    • 多重继承

A==》B

A==》C

B 和c可以同时被 D继承!

一个子类有多个父类

打印机

复印机-----打印复印一体机

人—》农民类

人—》工人类

农民工:继承了农民的属性和行为,也继承了工人的属性和行为

原因是什么?

FarmerWorker类继承于Farmer类,也继承于Worker类;Farmer类继承了Person类,Worker也继承了Pesron类;

那就说明在FarmerWorker中有两份来自于Person中的内容!一份来源于Farmer,一份来源于Worker,访问的时候就不清楚!

怎么解决:

虚继承!

原理是什么样子的?

验证对象占的内存空间:

虚继承额外的4字节在创建对象的时候是先分配的!

内存空间的分配就按如下图:

    • 类与类之间的关系

<1>.继承关系: is a

中学是学校的一个特殊版本

标签、编辑框、按钮这三种都是控件

<2>.包含关系: has a

人体内包含心脏!

登录界面是由标签、编辑框、按钮组成!

8、案例

封装了一个控件父类、窗口父类

作业:

  1. 整理笔记,思维导图

  1. 实现电子词典

  1. 封装了一个控件父类、窗口父类,让标签、编辑框、按钮去继承控件父类,登录类、注册类继承窗口父类

  1. 数据库封装 sqlite3 里面提供给的函数去封装

实现Open、添加、删除、修改、查找、close。

Vs2019中去配置sqlite3的环境!

Day6

    • 多态的概念

多态:多种状态、多种形态

水:水在不同环境下会呈现出不同的状态

C++中:调用同一个函数名,传入不同的参数,就会执行不同的函数体!

函数重载---其实就是一种多态

    • 静态(绑定)多态和动态(绑定)多态!!!

什么是静态(静态库):

在编译阶段会链接到可执行文件中

什么是动态(动态库):

在运行阶段会链接到可执行文件中

总结:

静态和动态的区别:链接的时刻不同

什么是静态多态:调用一个函数,在编译阶段就知道要执行的函数是哪一个

体现在:函数重载、运算符重载

G++去编译的时候,会根据函数的参数类型以及参数个数对函数进行重命名,调用的时候不同的函数名对应不同的函数体!

什么是动态多态:调用一个函数,在运行阶段才知道要执行的函数是哪一个

体现在:继承、虚函数

绑定:调用函数的语句与函数体进行绑定

案例:形状类—计算面积、周长

圆形类—计算面积、周长

矩形类—计算面积、周长

如果后期又要计算三角形的周长和面积,先定义一个三角形的类,换需要在定义一个计算周长和面积的函数与已经存在的函数互为重载!

这样子会造成代码冗余

解决方案:

既然圆形是形状、矩形也是形状、三角形也是形状,那能否用形状类类型来表示所有的形状,做函数参数呢?

子类是父类的特殊版本! 用父类来统一管理所有的子类!

问题:

并没有执行我们希望走的函数!

把子类对象的地址赋值给了父类类型的指针变量,通过父类类型的指针或者引用去调用成员函数,发现执行了子类从父类继承来的成员函数!并没有执行子类自己定义的成员函数!

怎么解决呢?

把父类的对应成员函数变成虚函数;

就是在函数的首部前面 加上一个关键字 virtual。

虚函数的格式:

Virtual 函数类型 函数名(参数列表) {}

子类继承了父类的虚函数,子类中对应的函数也变成了虚函数!子类中这个关键字virtual可以加也可以不加。

总结一下实现动态多态的条件:

  1. 一个父类多个子类

  1. 继承关系中,子类要重写父类的虚函数;函数首部是一模一样

  1. 用父类的指针或者引用去操作子类对象,调用虚函数才会触发动态!

如果只有虚函数,能触发多态?

    • 动态多态的实现原理!!!

为什么把父类的函数写成虚函数,通过父类的指针或者引用操作子类对象,调用虚函数就实现了多态呢?

虚函数表:这个表里面可以存很多个虚函数的地址;函数指针数组(数组里面存的是虚函数地址)。既然是数组,数组就需要占内存空间的!

函数指针数组什么时候存在的?当一个类中有了虚函数,这个类中就有一个虚函数表(函数指针数组)!。

父类中有虚函数,被子类继承后,在子类中对应的函数也是虚函数!父类有虚函数表,子类也有虚函数表!

子类没有重写虚函数:

子类重写了虚函数:

父类中有虚函数,被子类继承后,在子类中对应的函数也是虚函数!虚函数表中保存虚函数的地址;

如果子类有重写父类的虚函数,那么子类虚函数表中对应函数的地址就变成了子类重写后虚函数的地址,当通过父类的指针或者是引用去调用虚函数的时候,实质是根据虚函数表中的地址去执行了对应的函数!

如果子类中没有重写父类的虚函数,子类虚函数表中存的函数地址是父类中虚函数的地址!

    • 多态中存在的问题!!!

把子类对象的地址赋值给父类的指针变量,通过父类的指针去释放子类对象的时候,发现只走了父类的析构函数,并没有执行子类的析构函数!就会造成内存泄漏的问题!

如何解决:

把父类的析构函数写成虚函数!

面试题目:

在设计类的时候,为什么要把父类的析构函数写成虚析构函数?

因为如果创建子类对象的时候,在构造函数里面有通过new的方式去动态分配内空间,通过父类的指针去释放子类对象,只走了父类的析构,子类的析构没有执行, new出来的空间没被释放掉,就造成了内存泄漏!

为什么不执行子类的析构函数,就会造成内存泄漏呢?

析构函数的作用是做对象释放前的资源回收以及内存清理工作,不执行析构,资源释放不完全!

    • 重定义、重载、重写的区别?--面试题目

重定义(隐藏): 在继承关系中,子类有和父类同名的成员函数,父类的函数被隐藏了!同名:函数名相同即可!

不加virtual是重定义

何时重定义:父类的成员函数实现的效果满足不可子类需求

重写(覆盖): 在继承关系中,子类重写父类的虚函数,父类的虚函数在这个子类中被覆盖了!

函数的首部是一模一样的!

重载: 必须在同一个作用域中,函数名相同,功能相似,参数不同,与返回值类型无关的一组函数互为重载!

    • 抽象类

什么是抽象类呢?

当一个父类中的成员函数我们不知道该如何去实现的时候,但是每一个子类中都需要,并且实现的方法都是不一样的!这个时候父类中的成员函数就可以不用实现了,并且还要能够实现多态,所以必须存在,并且必须是虚函数!

此时就把这个函数写成纯虚函数!

纯虚函数的格式:

virtual 函数类型 函数名(参数列表) = 0;

总结:

  1. 抽象类就是一个类中如果有纯虚函数,这个类就是抽象类!

  1. 抽象类不能用来创建对象的

  1. 抽象类的作用就是用来被继承的!

  1. 如果一个类继承了抽象类,但是没有去重写并实现这个纯虚函数,这个类也是抽象类,仍不可创建对象!

    • 接口类

接口类其实就是抽象类的一种应用!

接口类的特点:

所有的成员函数都是纯虚函数!没有数据成员!

何时会设计接口类出来:描述一种能力或者是一些协议的时候!

飞机:飞行、降落

鸟类:飞机、降落

把共同具备的能力提取出来!

    • 案例

面试题目:谈谈你对多态的理解?

作业:

  1. 整理笔记,思维导图

  1. 数据库的封装:vs配置sqlite3

  1. 案例—完善一下,实现编辑框输入、光标上下切换

  1. 课堂练习去实操

  1. 复习C++的内容

  1. 考虑项目做什么?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值