C++基础知识--DAY4
今天主要讲的是类中除了构造器析构器以外的拷贝构造器,运算符重载等问题
首先是拷贝构造器
1. copy constructor(拷贝构造)
其也是构造器,其地位和constructor的地位是一样的
(1) 由普通数值做参数完成构造,constructor
这个就是一种构造,这种数值类型的构造器主要的列表形式为:
(2) 由同类对象做参数完成构造,copy constructor
拷贝构造器最简单的举例
如果不写那一段拷贝构造器则会发生报错,报错提示为:error: no matching constructor for initialization of 'Date'
Date d(2016,8,8);
因此此已经为一种深拷贝了,所以说需要我们认为构造拷贝构造器
另外一种情况,若上面的程序加入Date dd(d);dd.dis(),则这个也可以直接输出和d一样的数据,发生这种可以直接完成拷贝的原因是系统提供了默认的拷贝构造器,格式比较固定,一经实现,不复存在
(3)拷贝构造器的格式
class A
{
A(const A &another)
拷贝构造体
}
(4)拷此贝构造规则,不是空的,提供了一个等位拷贝机制
(5) 系统提供的默认拷贝构造器是一种浅拷贝,shallow copy
如果对象中不含有堆上的空间(指针指向的空间),此时浅拷贝可以满足需求,此时我们是不需要自实现的
(6) deep copy
如果对象中含有堆上的空间(通过*来实现),此时浅拷贝不能满足需求,需要自实现,浅拷贝会带来重析构,即double free
以下两种情况
这种情况是完全没有问题的,即申请一个对象,使这个对象指向堆上的一段空间,这段代码使用的是系统的默认存在的拷贝构造器
但是下列情况就是有问题的
这种情况存在的问题就是
因为等位拷贝的原因,比如在上面的代码中,我们由于在拷贝时采用等位拷贝,因此在拷贝时,将a1的地址拷贝给a2的地址,那么堆上的空间“C++ is the …..”就是同时被两个地址所指向,在进行析构时,两个地址均会进行一次析构,因此堆上的空间“C++ is the …..”会被释放两次,造成double free,因此此时我们需要自实现拷贝析构器
(7) 拷贝析构器实现的原理
如上所示,发生double free的原因是两个地址指向了堆上的同一段空间,如下图所示
因此为了解决这个问题,我们需要做的就是将堆上的空间拷贝一份,实现每个地址指向一段堆上的空间,并且两段堆上的空间的内容是一样的
因此首先应该是申请一段堆空间
深拷贝的原理即为
注意,两个指针的地址并未发生拷贝,而是首先在堆上申请了一段空间,然后将语句拷贝到新申请的空间中去,即a1,a2不进行复制拷贝等活动
(8) 同类对象方法中进行穿参可以访问其私有成员,其他则不可以,拷贝构造器主要用于传参和返值
另一种情况
也是属于拷贝构造器
传参的另一种情况
相当于实参到形參有一个拷贝,因此也是会发生两次构造,两次析构,但是如果改成引用,则只会发生一次析构。
2. 课堂实战,mystring
(1) sizeof(string)会出现版本的差异,我们主要考虑版本为4的情况
(2) string本质是对char *的包装
(3) 默认参数做标记位
构造顺序和析构顺序相反,先构造的后析构。
下面在程序代码的层面上对mysyting来进行具体的解释
a. 参见如下的代码
在代码中,我们并未对string定义任何的东西,但是我们最终的输出为空格和china这两个,说明C++内部有对string是有相关的定义的,因此我们利用mystring来对其内部机制做一个深入的理解
因此我们为了自己实现这个功能,我们需要写一个构造器,对于者两种情况,实际上是可以写两个构造器的,
对于string s来说,相当于是建立一个空的构造器,内部只有一个字符串\0,因此我们可以将其写为
即建立一个新的空间,在这个空间中拷贝上\0
对于string s1="china",这个是需要创建一个新的空间并对这个空间赋值
为了将这两种情况和并在一起,我们将最终构造器的代码写成如下
b. 上述情况是考虑的是数值等对其进行赋值,对于更多的时候,可能是对象对其进行赋值
把对象s2的内容赋给对象s1,为了实现这种赋值,我们引入拷贝构造器
c. 在上面的情况中,对于string s1="china",可以直接将字符串进行赋值的原因是因为我们对赋值运算符=进行了重载
d. 对于 > < == >= <=,C++的string中也对其进行了相应的定义
为了在mystring中相应的功能,我们编写了如下的程序
e. 我们可以见如下代码
对于字符串来说可以直接相加减也是C++相对于 C语言的扩展,因此我们也可以自己实现运算符重载+
f. 也可以实现+=的运算符重载
3. this 指针
系统在创建对象时,默认生成的指向当前对象的指针,其应用是避免了名相同的问题,如果重名了系统采用的是就近原则,可以实现串联调用
(1) this 的使用,在哪里可以用
打印出相同的地址,说明this确实是指向当前位置的指针
(2) this指针以什么样的形式存在
this作为函数参传隐式传进来,是不占用对象的体积大小
不论是否有this指针,Stu的大小是不变的
(3) 使用this指针有什么好处
下面存在一种问题
对于这种情况,系统会报错,因为他分不清楚是在私有成员的name还是在函数形參的name,因此我们常常使用this指针指明是具体的哪一个name
好处:
a. 避免形參与数据成员重名
b. 可以实现链式表达(串联,a=b=c)
c. 赋值运算符重载
用一个已有对象,给另一个已有对象赋值,两个对象均已创建结束后,发生的赋值行为
类名
{
类名&operator=(const类名&源对象)
拷贝体
}
class A
{
A& operator=(const A& another)
{
//函数体
return *this;
}
}
注意以下两种情况的区别
第一种情况是利用已有变量创建一个新的变量,第二种就是首先创建一个新的变量,再给其赋值
d. 系统提供默认的赋值运算符重载,也是一种浅赋值行为,如果对象中不存在由*构成的堆空间,此时默认也是满足需求的
赋值运算符重载的格式比较固定,A& operator=(const A& another),一旦发生自实现,即默认不存在
e. operator特性
系统提供默认的赋值运算符重载,一经实现,不复存在
A. 系统提供的也是等位拷贝,也就是浅拷贝,会造成内存泄漏,重析构
B. 要实现深深的赋值,必须自定义
自实现需要 1 自赋值
C. 解决的问题 2 内存泄漏
3 重析构
D. 返回引用,且不能用const修饰,string a,b,c; (a=b)=c; (a+b)=c
(4) 运算符重载= mystring operator = (const string & another)
(5) 运算符重载== > < >= <=
(6) 运算符重载+
+的运算符重载的原理是,本身是有两个变量的,然后我们将生成一个变量,但是生成变量的同时也会产生一个存储空间,因此我们首先将其释放掉,然后开辟一个两个字符串长度的空间
因此写出来的代码为
(7) 运算符重载+=
+=要做的事情是,假设本身有一个空间内部有10个存储单位,另一个空间内部有15个存储单位,现在要做的事情就是将这个有10个存储单位的空间扩容
realloc()的工作原理是:如果在所需要的分配的空间处有空间的话,那么就将直接将其扩容,如果在所需要分配的空间不够空间来分配,那么就在新的地方开辟一个空间,将原始的数据拷贝到新的空间中。
4. C语言返回栈对象
中间变量:下面这段程序
返回的a是存储在CPU中的
因此可以得出结论:栈上的对象是可以返回的,不可以返回栈上对象的引用,如上,如果返回对象的引用,因此相当于将其作用域扩展到main函数,但是刚刚扩展到main函数后,func这一段空间已经消失了。
(1) RVO/NRVO
(具名)返回值优化,是这么一种优化机制:当函数需要返回一个对象时,如果自己构建一个临时对象用户返回,那么这个临时对象会消耗一个析构函数的调用,一个复制构造函数的调用以及一个析构函数的调用的代价,通过优化的方式,可以减小这些开销,以下有几种情况
(a) 直接返回A(),不具名的情况
(b)具名情况
所得的结果在不同平台有差异,
优化的本质:在main函数调用foo之前,会在自己的栈帧中开辟一个临时空间,该空间的地址作为隐藏参数传递给foo,在需要返回对象A之前,就在这个临时空间构造一个A a,然后这个空间的地址再利用寄存器eax返回给main函数,这样main函数就能获得foo的返回值了
(c)赋值情况
这种情况所得的结果为:
说明在调用的时候,首先是r产生了一个空间,然后调用foo产生了一个空间,然后将foo产生空间的内容赋值给r空间,然后再进行析构。
5. 对象数组
(1) 对象数组中,有100对象,就会发生100次构造
(2) 构造器无论是重载还是默认参数,一定要把系统默认的无参构造器包含进来,不然生成数组的时候会有些麻烦
(3) 二段式初始化
在对象数组中,要求对象必须包含默认无参构造器的情况,但有时,默认无参构造器并不能完全满足我们的要求,可能需要再次初始化。
二段式初始化,常将默认无参构造器置为空,然后再次调用初始化函数
其中,对象数组就是二段初始化的原因之一
C++基础知识--DAY4的更多相关文章
- Python基础知识-Day4
一.函数关键字关键字是Python内置的,具有特殊意义的标识符,自定义标识符命名时不可与之重复.可以通过以下代码查看Python内置的关键字内容. import keyword print(keywo ...
- .NET面试题系列[1] - .NET框架基础知识(1)
很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...
- RabbitMQ基础知识
RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...
- Java基础知识(壹)
写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...
- selenium自动化基础知识
什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...
- [SQL] SQL 基础知识梳理(一)- 数据库与 SQL
SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...
- [SQL] SQL 基础知识梳理(二) - 查询基础
SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...
- [SQL] SQL 基础知识梳理(三) - 聚合和排序
SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...
- [SQL] SQL 基础知识梳理(四) - 数据更新
SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...
随机推荐
- mysql 清除大数据表单
背景:mysql数据库中有个日志表记录高达800多万,影响了mysql的正常业务访问,现需要清理三个月之前的所有数据,大概600多万(大概13G) 方法一:传统delete from xxx,传统,普 ...
- mac 电脑 打开隐藏文件
command +shift +. 第一次是打开,第二次是关闭
- 一、Dev单元格
二.获取表格数据 int selectRow = gridView1.GetSelectedRows()[0]; string id = this.gridView1.GetRowCellValue( ...
- SQL Server中获取指定时间段内的所有月份
例如查询 2012-1-5 到 2012-11-3 之间所有的月份 declare @begin datetime,@end datetime set @begin='2012-1-5' set @e ...
- 学习 Spring (十七) Spring 对 AspectJ 的支持 (完结)
Spring入门篇 学习笔记 @AspectJ 的风格类似纯 java 注解的普通 java 类 Spring 可以使用 AspectJ 来做切入点解析 AOP 的运行时仍旧是纯的 Spring AO ...
- poj-3080(kmp+暴力枚举)
题意:给你多个字符串,问你这几个字符串的最长公共子串是哪个,如果有多个,输出字典序最大的那个,如果最长的公共子串长度小于3,输出一个奇怪的东西: 解题思路:首先看数据,数据不大,开始简单快乐的暴力之路 ...
- Eclipse环境配置与快捷命令
1.VS.Chrome.Eclipse调试命令对比: VS: F5: 继续运行 F10: 单步执行 F11: 进入函数内部 Shift + F11: 由函数内部返回调用处 Chrome: F8: 继续 ...
- mvc HTML转Excel身份证后三位变成0
@{ var style = "vnd.ms-excel.numberformat:@"; } //HTML <td style=@style><span> ...
- 【XSY2716】营养餐 博弈论
题目描述 给你一棵有根树,每个点有两个属性\(a,b\) 两人轮流操作,每次要减小一个点的\(a\)值,要求 \[ a_x\geq\sum_{i\in child(x)}a_ib_i \] 保证初始状 ...
- haar的简单应用(2)
上次对图片进行了人脸识别,这次对摄像头捕获的内容进行识别 直接写注释来解释 import cv2 def CatchUsbVideo(window_name, camera_idx): #定义一个函数 ...