今天主要讲的是类中除了构造器析构器以外的拷贝构造器,运算符重载等问题

首先是拷贝构造器

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的更多相关文章

  1. Python基础知识-Day4

    一.函数关键字关键字是Python内置的,具有特殊意义的标识符,自定义标识符命名时不可与之重复.可以通过以下代码查看Python内置的关键字内容. import keyword print(keywo ...

  2. .NET面试题系列[1] - .NET框架基础知识(1)

    很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...

  3. RabbitMQ基础知识

    RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...

  4. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  5. selenium自动化基础知识

    什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...

  6. [SQL] SQL 基础知识梳理(一)- 数据库与 SQL

    SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...

  7. [SQL] SQL 基础知识梳理(二) - 查询基础

    SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...

  8. [SQL] SQL 基础知识梳理(三) - 聚合和排序

    SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...

  9. [SQL] SQL 基础知识梳理(四) - 数据更新

    SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...

随机推荐

  1. linux的使用

    第一 安装ubuntu操作系统 1. ubuntu下解决中英文输入法问题 问题: ubuntu在安装了搜狗输入法后无法切换英文,即使在搜狗输入法中设置了切换按键依然无反应, 原因在于当前系统中只有一个 ...

  2. Servlet3.0上传

    1.上传对表单限制 *method=post *Enctype=multipart/form-data,它的默认值是:application/x-www-form-urlencoded 表单中需要添加 ...

  3. C-Lodop提示“网页还没下载完毕,请稍等一下再操作.”

    该提示在Lodop旧版本中是: 提示"WebSocket没准备好,点确定继续",提示“C-Lodop没准备好”,新版本修改了该提示的描述“网页还没下载完毕,请稍等一下再操作.”,让 ...

  4. webpack始出来

    一直想好好整理一下webpack,现在就整理吧. 总结自己的实际搭建的整理情况,我还是要先对自己说一句,以后给文件夹起名字的时候不要用一些特殊的关键字,比如我在做这个demo的时候,我用的文件夹名称叫 ...

  5. Civil 3D 二次开发 名称模板不能正常工作

    using Autodesk.AECC.Interop.Land; using Autodesk.AECC.Interop.UiLand; using Autodesk.AutoCAD.Applica ...

  6. mysql 集群方案

    试试基于Galera的MySQL高可用集群  mha  mgr

  7. Android ProgressDialog 简单实用

    ProgressDialog progressDialog; @SuppressLint("HandlerLeak") Handler handler1 = new Handler ...

  8. re 正则表达式

    简介: 1.一堆带有特殊意思的符号组成的式子它的作用 处理(匹配 查找 替换) 字符串 2.在爬虫中大量使用 其实有框架帮你封装了这些复杂的正则 3.在网站和手机APP注册功能中大量使用,例如判断你的 ...

  9. 序列化模块组 pickle,json , xml , shelve , configparser

    序列化是什么? 序列化指的是将内存中的数据结构转化为一种中间格式 并存储到硬盘上. 反序列化是什么? 将硬盘上存储的中间格式数据再还原为内存中的数据结构. 为什么要有序列化? 是为了将数据持久存储 较 ...

  10. ☆ [HDU2089] 不要62「数位DP」

    类型:数位DP 传送门:>Here< 题意:问区间$[n,m]$的数字中,不含4以及62的数字总数 解题思路 数位DP入门题 先考虑一般的暴力做法,整个区间扫一遍,判断每个数是否合法并累计 ...