173-C++面向对象高级开发-上(侯捷)

1、c++编程简介

在这里插入图片描述

类中带有指针和类中不带有指针

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HKLzaLH-1652009709589)(../../assets/c++面向对象高级开发-上/image-20220222101048676.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LWYCZl9k-1652009709590)(../../assets/c++面向对象高级开发-上/image-20220222101105577.png)]
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、头文件与类的声明

c++将数据和函数包在一起。

在这里插入图片描述

经典的class分类,带指针和不带指针的,具有代表性的就是我们写的
两个案例: 复数案例和字符案例
复数案例: 数据有很多份,函数只有一份(复数的加减乘除…)
字符案例: 里面只有一个指针,数据用指针指出去,这是这两点的不同
ppt中的字符串的大小,每个里面只有一个指针

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

标准库用的是<>,自己的头文件用的是“”

在这里插入图片描述
在这里插入图片描述

防卫式声明:

在这里插入图片描述

写的任何一个头文件,最好都加一个防卫式声明。

在这里插入图片描述

在这里插入图片描述

模板表现形式:现在是什么类型还没有决定;

类模板需要提供类的类型。

在这里插入图片描述

3、构造函数

在这里插入图片描述

inline:内联函数
	函数在类中定义完成,就叫做inline;
	简单来说,若果程序太复杂,就没有办法inline;(能不能编程inline,由编译器自己决定)

在这里插入图片描述

函数在本体中就像成了一个inline,函数是inline会很快 
外面的函数我们可以自己加上inline,能不能成inline由编译器自己决定。

在这里插入图片描述

所有的数据都应该是private;外界需要调用的函数,用public;
处理自己数据的函数可以用private;

在这里插入图片描述

构造函数:
	函数名和类名相同
	不需要有返回值

**构造函数的赋值:**要培养好的,大气的写法

不要这样写:

在这里插入图片描述

用c++构造函数的特殊写法—初始化列表(构造函数独有的):

在这里插入图片描述

总结: 一个变量的值的设定:有两个阶段,初始化(在大括号之前,效率比较高)和赋值(在大括号中)。

不带指针的类(系统会自己提供析构函数),多半不用写析构函数

在这里插入图片描述

**函数重载发生的条件:**参数值,类型;返回值不能作为重载的条件

在这里插入图片描述

构造函数1和2,2是对1进行的重载,此时如果在右边进行创建对象时,

编译器也不知道该调用哪个。

4、参数传递和返回值

构造函数写在private中(表示不可以被外界调用的):

在这里插入图片描述

设计模式:单例模式Singleton

在这里插入图片描述

在小括号的后面加上 const(位置:小括号后面,花括号前面):只读不写

在这里插入图片描述

const complex  c1(2,1):变量或者对象的内容是不可以改变的
注意:函数处的const和创建对象时的const都需要加const
参数传递最好用引用进行传递,引用传递不希望将自身进行改变,
在引用的前面加上const

在这里插入图片描述

在这里插入图片描述

基本标准:
	1. 数据在private
	2. 参数传递尽可能用引用进行传入
	3. 函数返回值尽可能用引用
	4. 函数和对象该加const处要加const
	5. 构造函数使用初始化参数列表

在这里插入图片描述

在这里插入图片描述

相同的class可以互相调用参数和成员函数。

在这里插入图片描述

什么情况下不能return by reference

在这里插入图片描述

5、操作符重载与临时对象

c++允许对操作符进行重载:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqm34rI0-1652009709606)(…/…/assets/c++面向对象高级开发-上/image-20220222105148361.png)]

任何一个成员函数,都会隐藏一个this指针(虽然没有写,但是它是在的),谁调用函数就是this,this就指向谁;
写代码时,this是不能写,写出来就错了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jpUX3017-1652009709606)(…/…/assets/c++面向对象高级开发-上/image-20220222105539073.png)]

传递者无需知道接受者是以“引用”的方式进行传递。

下面的引用返回值:可以满足操作者的连续操作c3+=c2+=c1。

全局函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IFVCh2Kc-1652009709607)(…/…/assets/c++面向对象高级开发-上/image-20220222105624702.png)]

**注意:**全局函数没有this指针;

image-20220222105652131

这里函数返回值没有用引用进行接收,因为,return返回的是临时对象

image-20220222110307916
因为这里函数中会创建一个新的对象,来接收相加的值,如果用引用,函数调用结束后,本体会消失,引用出错,因此用complex作为返回值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJOXfKHZ-1652009709607)(…/…/assets/c++面向对象高级开发-上/image-20220222110515605.png)]

临时对象,函数结束就会消失,不用怕,因为会返回出去。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r59K55jE-1652009709607)(…/…/assets/c++面向对象高级开发-上/image-20220222110534411.png)]

complex();     -- 临时对象,没有参数,就是默认值0;
complex(4,5);  -- 也是临时对象;
这两个临时对象,进行到下一行,这两个对象均没有了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgE2FD8K-1652009709608)(…/…/assets/c++面向对象高级开发-上/image-20220222110646935.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dHSQhgO2-1652009709608)(…/…/assets/c++面向对象高级开发-上/image-20220222110725219.png)]

**注意:**这里是可以用引用的(并没有产生一个loacl对象),这样不是最好的。

等号“=="的重载:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pBTk3IRV-1652009709609)(…/…/assets/c++面向对象高级开发-上/image-20220222111041132.png)]

不等号“!=”的重载:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TAqjMZJG-1652009709609)(…/…/assets/c++面向对象高级开发-上/image-20220222111220477.png)]

共轭复数:实部相等,虚部相反数

注意:任何一种实现都可以将其写成全局函数成员函数,都可以解决问题;

重载“<<”:

”<<“ 不可能认识一个新的类型,将其输出到屏幕上进行重载(重载它只能使用全局的方式)。

cout的类型是ostream,ostream前面不能加const,实际上在往函数中的os每丢任何东西都在改变os的状态。之后再return后面想怎么输出就怎么输出了。

**同样:**返回值可以用void,想远一点,使用者可能想连串使用<<,因此应ostream&,返回引用进行输出。ostream&前面不能加const,因为在连串使用时,状态会发生改变。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OHDAQM03-1652009709610)(…/…/assets/c++面向对象高级开发-上/image-20220222111627082.png)]

c++是自由形式,加几个空格都无所谓。

总结:
设计一个class需要注意的事情:
	1. 构造函数的初始化;
	2. 函数该不该加const一定要考虑;
	3. 参数的传递尽量考虑“引用传递(pass by reference)”,以及前面要不要加const;
	4. 返回值尽量考虑“引用传递(pass by reference)”,以及前面要不要加const;
	5. 数据放在private,函数大部分放在public;

6、复习complex类的实现过程

编程的好习惯:

1、 防卫式的常数定义

image-20220222111955846

2、数据私有化private,数据的类型double

3、class的构造函数,构造函数初始化列表 {}:可用于其他文件开辟内存,生成文件等。

image-20220222112424479

4、重载+=符号,对类进行加法,重载函数有两种形式

  • 成员函数
  • 全局参数

成员函数的好处:

返回值可以用引用,调用时可以使用成员函数独有的this指针,
因此重载函数只需一个参数,默认的this指针 ,谁调用就是谁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HdVjHYCO-1652009709610)(…/…/assets/c++面向对象高级开发-上/image-20220222112611683.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jfXMaHFt-1652009709611)(…/…/assets/c++面向对象高级开发-上/image-20220222112639099.png)]

**全局函数:**返回值不能用引用,需要传入两个参数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xjn9Tkxj-1652009709611)(…/…/assets/c++面向对象高级开发-上/image-20220222114114354.png)]

5、函数后面加上const;

image-20220222114145093

如果函数后面并没有改动private中的数据,就需要在()加上const。

6、友元:可以访问class中的私有成员

image-20220222114540299

7、inline函数

​ 可以在类内,也可以在类外定义。

8、类内声明的函数,在类外实现

  • 加上complex::类作用域;
  • 类内的成员函数自带this指针,谁调用就是谁;
  • 只需传+=右边的参数,用引用方式传入;
  • 该参数无需改变,在前面加上const;
  • 返回值可以用引用,因为是+=,将值给已经存在的对象;
  • 将接受到的参数给另一个参数去做;return _doapl (this,r);
  • inline函数,你自己加,到底编译器加不加,看编译器。

这里是可以传引用的,因为左边本来是有一个的this指针的(不是局部变量);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EMPIkc7A-1652009709612)(…/…/assets/c++面向对象高级开发-上/image-20220222120004449.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uA6wPD3G-1652009709612)(…/…/assets/c++面向对象高级开发-上/image-20220222120011112.png)]

9、全局函数

加法操作,为什么用全局函数,不用成员函数?
    加法包括:复数+实数,实数+复数,复数+复数,比较复杂,
    如果用成员函数,只能复数和复数相加,因此用全局函数 ;

类名+():表示临时对象,代码执行到下一行就会被释放;

这里它用了返回值进行返回,这样就可以对它的值进行返回

返回值用的complex,不能用引用(这里return的是一个临时变量)

image-20220222120227811

10、“<<”操作符重载(写成非成员函数)

  • 操作符重载一定是作用在左边的操作数上;
  • 传入的x可以使用引用方式,并且前面加上const;
  • ostream(是一个类)前面不可以加const,因为os的状态是一直发生变化的;
  • <<使用者如果想连串的使用,返回值设置为引用的类型ostream&;
image-20220222120356377

7、三大构造函数:拷贝构造、拷贝赋值、析构

**另外一个经典的例子:**class中有指针 ,用字符串string来进行设计

拷贝构造和**拷贝赋值:**前面的complex例子,如果类中没有写,编译器起始内部给你写好了,复数比较简单,类中没有指针,不需要写,用编译器给的就足够了。

class中带指针,一定要自己写 拷贝构造 和 拷贝赋值。

image-20220222170606602

拷贝构造和拷贝赋值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEO1KdgE-1652009709612)(…/…/assets/c++面向对象高级开发-上/image-20220222170636278.png)]

String s3(s1);    //拷贝构造
s3 = s2;    //拷贝赋值

字符串设计成指针类型,可以进行动态的开辟空间;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfgvaxR8-1652009709613)(…/…/assets/c++面向对象高级开发-上/image-20220222170721638.png)]

构造函数:String(const char*cstr)=0;
拷贝构造函数:String(const String& str);
拷贝赋值函数:String& operator=(const String& str);
析构函数:~String()

7.1、构造函数

String(const char* cstr)=0;
  • 传入一个指针,判断其是否为空,如果不是空,采用strlen确定字符创的大小+1(最后还是有一个结束符号’\0’的,需要+1);
  • 用完之后,需要在析构函数中,delete对内存进行释放,否则会造成内存泄漏;
  • 使用strcpy进行赋值字符串。
image-20220222171031195

构造函数中传入一个指针:为什么传入一个指针?

从c语言延续的概念,

字符串:一个指针指着头,后面有一串,最后面有个"\0"这样的结束符号。

字符串的大小:(怎么设计?)

  • 不知道有多长,后面有个结束符号,可以知道有多长;
  • 后面没有结束符号,前面有长度这个整数。

7.2、析构函数

将开辟的内存空间进行释放,否则就是内存泄漏了;

调用3次析构函数。

image-20220222171945907

7.3、拷贝构造函数

类中数据有指针,就必须有拷贝构造和拷贝赋值

​ 如果使用浅拷贝操作带有指针的class,因为ab中都是指针,都会指出去,从而得到数据如果对其进行浅拷贝,会使得两个指针指向同一块数据,原来的数据会单独存在,造成内存泄漏,两个指针指向同一块区域,在进行内存释放时,会重复释放,出错!

image-20220222172418727

上面使用的是默认的拷贝构造和拷贝赋值;

深拷贝:

image-20220222172803848

7.4、拷贝赋值函数

image-20220222172840050

注意:成员函数是有this指针的。

  1. delete[] m_data,首先将新空间进行清空;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJNyx2w1-1652009709613)(…/…/assets/c++面向对象高级开发-上/image-20220222173215423.png)]

  1. m_data=new char[strlen(str.m_data)+1],重新将新空间进行开辟,和传进来的空间一样大;
image-20220222173247511
  1. **strcpy(m_data,str.m_data),**将传入的字符串赋值给新的字符串
image-20220222173313795

注意:一定要检查是否进行了自我赋值,检查是不是出现了s3=s3这种情况(不用进行后续部分,效率高)

image-20220222173352357
if(this == &str)//检测自我赋值  &str表示取地址
	return*this
	
注意&符号的使用:
	出现在typename后面:string& str   表示引用;
	出现在对象前面:&str   表示取地址。
image-20220222173439086

​ 如果两个对象是相同的,进行了拷贝赋值操作,开始时,两个相同的对象指向同一块内存,如果将其删除之后,这是后就会出错,他需要靠另一个对象的长度,此时找不到另一个,报错!

7.5、ostream

class中定义的 get_c_str() 成员函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHYcuIm4-1652009709614)(…/…/assets/c++面向对象高级开发-上/image-20220222173701958.png)]

这里重载了"<<"符号。

image-20220222173743058

8、堆、栈与内存管理

8.1、栈和堆

**栈:**存在于某个作用域的一块内存空间;(函数{}里面就是一个作用域)

image-20220224115032903
Complex c1(1,2):
	开辟在栈区,程序指向结束后,会自动调用析构函数,将其释放.
Complex* p=new Complex(3):
	在堆区开辟一块内存,将创建的对象放入,堆区空间由程序员手动开辟,手动释放,不会自动释放。

8.2、栈对象的生命期

image-20220224115142106

c1存放在栈区,函数的作用域结束(就是离开大括号之后就没有了)之后结束,自动释放,调用析构函数。

8.3、静态对象

生命在作用域结束后仍然存在,直至整个程序结束

image-20220224115243912
析构函数在整个程序结束时调用。

8.4、全局对象

image-20220224115329668
析构函数在整个程序结束时调用。

8.5、堆中创建对象

image-20220224115437517

右图:没有释放自己开辟的内存,会造成内存泄漏

**注意:**指针在离开右边大大括号之后,指针会死亡,但是开辟的空间还是存在的,对该内存失去了控制,内存泄漏。

8.5.1、new操作

三个步骤:

  • 分配内存,是void*类型
  • 将void型的指针转换为Complex型的指针
  • 最后调用构造函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5gvkgdFI-1652009709614)(…/…/assets/c++面向对象高级开发-上/image-20220224115604181.png)]

new:先分配内存,再调用构造函数。

operator new是一个函数,这个函数的内部是malloc函数,相当于调用malloc函数;

8.5.2、delete操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f6X4q0Pa-1652009709615)(…/…/assets/c++面向对象高级开发-上/image-20220224115754901.png)]

delete操作:先调用构造函数,再释放内存。

  1. 调用析构函数,**将字符串里面动态分配的内容给杀掉,**字符串本身只是一个指针而已;

  2. c++调用operator delete ,源代码内部调用的是free(ps),将字符串本身(指针)给杀掉

8.6、动态分配所得的内存块(memory block)in VC

8.6.1、Complex类分配内存—类中无指针

image-20220224120031974

**每一小格子:**4bit

调试模式下:(左1)

​ 创建一个Complex对象:本身:绿色8bit + 灰色:上面8个+下面1个(8*4+4=32bit)+上下2个橘色的cookie(2*4=8)=52。

​ 由于vc创建的大小必须是16的倍数,因此需要将52填补成64(用图中pad进行填补,蓝色的)

问题:内存这么大,产生浪费?

浪费是必要的浪费,以后进行回收时,方便回收。

学生一般不会进入调试模式(下图为非调试模式(没有灰色的)16个bit,正好),16的倍数。

非调试模式:(左2)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-03nrBfQY-1652009709615)(…/…/assets/c++面向对象高级开发-上/image-20220224120314642.png)]

**上下cookie的作用:**最主要的是记录整块给你的大小,因为在释放时,只会给你一个指针,知道总大小,才方便释放。

00000041:

​ 采用的是16进制,4表示:4*16^1=64,最后一位的表示内存状态(1表示内存给出去了,0表示还给操作系统)

8.6.2、string类—类中有指针

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8P8dEUit-1652009709615)(…/…/assets/c++面向对象高级开发-上/image-20220224120542857.png)]

对于string类,字符串本身内含一个指针,大小为4。

调试模式下:(左3)

​ **分配内存大小:**本身4+加上灰色的(上面32+下面4)+上下cookie(4*2) = 48

00000031:采用的是16进制,3表示:3*16^1=64,最后一位的表示内存状态(1表示内存给出去了,0表示还给操作系统)

非调试模式下:(左4)

分配内存大小:本身4+上下cookie(4*2)+pad填补的4=16,正好是16的倍数;

8.6.2、array动态分配

image-20220224121133407
如果开辟的是数组char[],则在释放空间时,需要delete [],使用中括号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SXygnwt7-1652009709616)(…/…/assets/c++面向对象高级开发-上/image-20220224121207648.png)]

内存分析:

**左1:**3个复数(3x8,灰色部分)+调试模式(32+4,黄色部分)+上下2个cookie(4x2)+4(左1中的3:用来记录有3个复数)=52,将其扩展为16的倍数:80(利用蓝色的pad进行扩充)。

**内存大小为80:**采用的是16进制,3表示:5*16^1=80,最后一位的“1”表示内存状态(1表示内存给出去了,0表示还给操作系统)。

**左2:**非调试模式下;

**左3:**string调试模式;

**左4:**string非调试模式;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbCxn6XJ-1652009709616)(…/…/assets/c++面向对象高级开发-上/image-20220224142907824.png)]

**使用了[]之后,**系统知道是要删除的是数组(没有写[],编译器会理解成只有1个,只会调用1次[]),因此会调用3此析构函数,将其各个元素动态开辟的空间删除。

**注意:**字符串占用的空间释放了,是字符串的元素在堆区开辟的空间没有一一释放。

9、复习String类的实现过程

  1. 防卫式定义

    image-20220224143105147
image-20220224143115954
  1. 设计字符串时,由于其大小不好指定,因此里面放指针

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fHBosGjX-1652009709616)(…/…/assets/c++面向对象高级开发-上/image-20220224143145029.png)]

  2. 构造函数、拷贝构造、拷贝赋值、析构函数、访问字符串函数get_c_str()的声明;

  3. 在类外写构造函数和析构函数;

image-20220224143225079

使用strlen和strcpy时需要包含头文件:#include <string.h>

array new一定要搭配array delete,需要带中括号。

记得不管怎样,都要在类外函数中加—inline(不管编译器能不能帮你做成inline函数)

  1. 拷贝构造函数
image-20220224143724328
  1. 拷贝赋值函数

image-20220224143748799

  • 希望进行连串的操作,因此返回值类型用string&;

  • 成员函数是自带this指针的;

  • 拷贝赋值一定要注意是不是自我赋值

10、类模板

10.1、static函数补充

image-20220224143955011

成员函数会有一个隐藏的this指针;

非静态成员:

  • 通过this指针调用c1、c2、c3;
  • c1.real()和complex::real(&c1)等价的。
image-20220224144223842

静态成员(静态变量、静态成员函数):

  • 加了static成为静态变量之后,就和对象脱离了;
  • 不属于对象,在内存的某个区域,单独的存在一份;
  • 这种静态的数据只有一份。

**静态变量使用情况:**比如去银行开户,100万人,他们的利率都是一样的,因此需要经利率设置为静态变量。

**静态函数使用情况:**没有this指针,不能像一般的成员函数去访问、处理对象;静态函数,如果要处理数据,只能处理静态数据。

image-20220224144355936

静态变量的声明和定义:

  1. 在类内对静态变量进行声明:
static double m_rate;
  1. 类外对静态变量进行定义:
double Account::m_rate=8.0;

静态函数的调用:

  • 通过对象调用;
  • 通过类型调用;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cwdy39PE-1652009709617)(…/…/assets/c++面向对象高级开发-上/image-20220224145222118.png)]

10.2、单例设计模式

**单例设计模式:**将构造函数放在private中;

image-20220224145319191

在private中放一个static A a自己,已经创建了一个a;

不想让外界创建A(将构造函数放在private中),这样A就只有一份a;

**缺点:**如果外界都没有用到,a就是一种浪费。

更好的写法:

image-20220224145414929

将只有一份的A放到成员函数中,因为构造函数是私有化,因此只有一份;而且,只有调用getInstance成员函数时,才会创建 a。

10.3、cout的补充**(为什么cout可以接受各式各样的数据?)**

  • 对里面很多的操作符进行了重载,能接受很多操作符;
  • cout是一种ostream,继承的ostream
image-20220224145527688

10.4、类模板

在调用时,必须使用**<>**指出传入的类型:complexc2(2,6)

image-20220224145718781

需要指定模板的类型<数据类型>;

complex<int>c2(2,6)
image-20220224150323144

10.5、函数模板

image-20220224150347103

运算符重载:在stone类内重载了小于号

10.6、namespace 补充

image-20220224150433082 image-20220224150454652 image-20220224150504191

在这之前谈的就是对单一的class怎么写的 ,接下来就是面向对象编程,类和类之间的关系。

11、组合与继承

11.1、Composition—复合

image-20220224150544082

复合(Composition):一个类中有另外一个类;

image-20220224151335991

queue:队列,底层是deque

deque:双端数组

queue内含一个deque;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5DU2iRz-1652009709617)(…/…/assets/c++面向对象高级开发-上/image-20220224151518462.png)]

黑色菱形表示,它里面有其他东西;(本例中,queue中有deque)

image-20220224152124965

queue类中包含deque类型,可以调用deque中的函数,Adapter设计模式。

**注意:**这里queue里面的函数大多数都是直接使用的是deque容器里面的;

11.1.1、复合的内存分析

**黑色菱形+箭头:**表示一个里面包含另一个;

image-20220224152255595

Container里面包含Component,黑色菱形表示一个里面包含另一个;

未命名图片
11.1.2、复合类型构造和析构时机
  • **构造时:**先构造内部,再构造外部;
  • **析构时:**先析构外部,再析构内部。

都是编译器帮我们安排好的。

11.2、Delegation/Composition by reference—委托

一个类中包含另一个类的指针。

**两个类的生命是不同步的,**需要调用指针时,才创建;

前面的复合(Compositon),两者生命是相同的。

**空心的菱形表示指针:**String里面包含StringRep的指针

未命名图片

11.3、Inheritance—继承

**空心三角+直线:**表示继承关系;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F345qyfJ-1652009709618)(…/…/assets/c++面向对象高级开发-上/image-20220224153102946.png)]

**右上角的"T":**表示的是模板的意思;

未命名图片

继承:父类数据会被完整的继承下来。

11.3.1、继承的构造和析构函数之间的关系

未命名图片

子类继承父类,子类的数据范围 > 父类

  • **构造函数:**先构造父类,再构造子类;
  • **析构函数:**先析构子类,再析构父类。

父类的析构函数必须是虚函数;

image-20220224154440349

只要类会变成一个父类,或者将来会变成一个父类,就将它的析构函数设置为虚函数。

12、虚函数与多态

12.1、虚函数—virtual function

  • **非虚函数:(**没有加virtual的一般成员函数)不希望子类重新定义父类函数;
  • 虚函数:(在成员函数前面加上virtual,成为虚函数)父类中已经有默认定义,希望子类重新定义它;
  • 纯虚函数:子类一定要重新定义纯虚函数(父类中没有默认定义)。父类中的纯虚函数,子类一定要复写它
image-20220224155248453 image-20220224155551597 image-20220224155559002

创建一个子类对象,通过子类对象调用父类函数;

注意父类CDocument中的OnFileOpen函数中的Serialize函数实际上是虚函数,上面没写出来;

在子类CMyDoc中将其进行了重写,通过子类对象调用父类中的OnFileOpen函数,开始执行父类中的OnFileOpen函数,指向到Serialize函数时,发现是虚函数,到子类中找Serialize函数具体的实现。

**总结:**虚函数,将父类中的函数延缓到子类中去决定。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3l328ISj-1652009709618)(…/…/assets/c++面向对象高级开发-上/image-20220224155701867.png)]

12.2、继承+复合的构造和析构顺序

  1. 子类中有其他类型+子类自己还是继承的父类
image-20220224155834367 image-20220224155850436
  • **构造函数:**Base构造函数—>Component构造函数—>Derived构造函数
  • **析构函数:**Derived析构函数—>Component析构函数—>Base析构函数

先构造和析构复合的成员。

  1. 子类继承了父类+父类中有其他类型
image-20220224160017201

**构造顺序:**如框图所示(从里到外),先父类中的其他类型构造Component—>父类构造—>子类构造;

**析构顺序:**如框图所示(从外到里),子类析构—>父类析构—>父类中的其他类型析构Component

12.3、委托+继承实例

image-20220224160151536

文件只有一个,窗口有4份。

如果数据有变化,相应的各个表示的图都会跟着变化。

image-20220224160633471

subject:原始的数据,里面可以包含多个Observer指针(委托的形式)

Observer:观察数据,以不同窗口形式进行表示;还是一个父类,可以派生出不同的子类;

**vector<Oberserver*>m_viewers:**准备一个容器来放右边各个不同的Observer,Observer;

**Subject中attach()函数:**将观察者放入Subject中的vector容器中;

**notify():**通知所有的观察者Observer更新数据。

每一个Observer窗口都会来父类进行注册,注销;

**注册:**提供函数放到容器中,进行注册,调用数据;

image-20220224161011117

**注销:**同时还应该进行注销

notify函数(遍历函数):更新数据

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liufeng2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值