大家好
欢迎回来继续学习
C++语言程序设计
这一节我们来学习复制构造函数
复制构造函数是什么呢
为什么我们需要复制构造函数呢
我们还是首先温故而知新
回忆一下
当我们定义一个
基本类型的变量的时候
是不是经常会用一个已经存在的
已经有值的这个变量
去初始化新定义的变量
我们定义对象的时候
也会经常有这样的需求
就是用一个已经存在的对象
去初始化新对象
这个时候
怎么实现这种初始化呢
像基本类型的数据
怎么样用已经有值的变量
去初始化同类型的新变量
这个编译器它能知道
因为什么
因为基本类型数据
是语法预先定义好的
而我们自定义的类的类型
它该怎么初始化
当然编译器不会默认知道的
这个时候怎么用一个对象
去复制新对象
就需要由我们自己来定义
C++语法为我们提供了一种
特殊的构造函数
叫做复制构造函数
在复制构造函数中
我们可以规定
如何用一个已经存在的对象
去初始化一个新对象
这个已经有了对象
可以用它的引用作为
构造函数的参数
当然了
我们在前面例子中
看到有些类的定义
并没有定义复制构造函数
那么如果我们不定义
复制构造函数会怎么样呢
如果你在定义类的时候
没有定义复制构造函数
编译器也会为我们生成一个
默认的复制构造函数
那么这个默认的复制构造函数
它干事吗
会不会也像默认的构造函数似的
什么初始化工作都不做呢
不是的
默认的复制构造函数
它还真干事
它会实现两个对象的
数据成员之间的一一对应复制
那么这样的一一对应复制
很多时候就已经令人满意了
那么我们就不用写
复制构造函数了
但是在一些时候呢
这个默认的复制构造函数
还不那么令人满意
那么这时候我们就可以利用
复制构造函数这个机制
我们自己来规定
如何进行复制构造
现在我们就来看看
程序中如何自己定义
复制构造函数
复制构造函数它是一种
特殊的构造函数
它的形参必须是本类对象的引用
那么复制构造函数的作用
就是用这个形参构造新的对象
那我们来看
在类里面
我们可以定义普通的构造函数
它的函数名就是类名
有一个形参表
这个形参表
可以是任意格式的形参
任意类型的形参
任意个数的形参都可以
但是接下来我们来看
另一个构造函数就很特殊了
它的参数类型
必须是本类对象的引用
而在这个参数前面呢
又加了一个const来限定
为什么
因为我们在第三章中学过
当我们传递引用作为参数的时候
实际上是可以双向传递数据的
也就是说
接收这个引用参数的函数
它在函数体中
如果对这个形参引用
做了任何修改
那么实参也会被同步修改
显然这不是我们写
复制构造函数的目的
我们写复制构造函数
是希望用形参引用
所指的那个对象
去初始化新对象
但绝不希望在这个初始化过程中
把原有的那个形参对象给修改了
那么怎么办呢
在这加一个const关键字
这就说明了这个引用
是一个常引用
我们只能使用这个引用
去读取它里面的数据
但是不能用这个引用
对它指向的对象进行修改
这样既能够传参数进来
又能够保证实参的安全性
接下来我们看
这个复制构造函数
在类外实现的时候
它跟其他的构造函数
实际上是一样的
前面要有个类名
后面函数名
复制构造函数的函数名也是类名
同样它不允许定义返回值
函数体中也不允许有return语句
除了在构造新对象的时候
用已有的对象做参数
去初始化它
这种情况以外
还有什么情况下
复制构造函数有可能被调用呢
一共有三种情况
是典型的要调用
复制构造函数的情况
我们来看看哪三种情况
首先当然就是我们知道的
定义一个对象的时候
用本类的另外一个对象
作为初始值
这个时候显然是发生了复制构造
第二种情况呢
就是如果一个函数的参数
是类的对象
那形参首先规定
它是一个类的对象
那么调用函数的时候呢
给出的实参
也应该是同类型的对象
这个时候用实参去初始化形参
做形实结合的时候
就发生了复制构造
还有一种情况
就是如果一个函数的返回值
是类的对象
函数执行完了要返回主调函数了
这个时候它有一个return语句
return语句中返回一个对象
给主调函数
那么在这个过程中
也是有可能发生复制构造的
如果我们在定义一个类的时候
没有为它定义复制构造函数
这个时候编译的时候
编译器就会生成一个
默认的复制构造函数
这个默认的复制构造函数的功能
就是用参数引用
所指向的这个对象
去初始化新对象
直接将数据成员一一对应
就这么一一对应着复制
很多时候呢
这样的功能也就够用了
在这一章呢
我们遇到的例题习题都比较简单
大家会直觉地感觉
这个默认的复制构造函数
就挺好用了
那我们还需要自己定义
复制构造函数吗
好
如果现在你写简单程序的时候
觉得自定义复制构造函数
没有必要
用默认就挺好
那你可以先这样用
后面当我们在第六章的时候
我就会给大家看相关的例题
如果类的成员中有指针的时候
很多情况下
默认的复制构造函数
它实现的浅层复制功能
就不够用了
这时候我们就需要做
深层的复制构造
那即使对于简单的程序
我们也可以再想一想
会不会
你有不同的复制构造的需求呢
比如说我们使用复印机的时候
有可能是要缩小复印
放大复印
把彩色的复印成黑白的
或者说覆盖住一部分
复印另一部分 都有可能
这是平常我们
进行复印的时候的经验
在程序中
当我们复制一个对象的时候
是不是也有可能说
我要做特定功能的复制构造
并不希望
原样不动地一一对应复制
而是只复制它的一部分功能
或者复制的时候
给它做一个小小的修改
我们可以说
这是个打引号的复制
只要你使用复制构造函数
就可以按照你自己的意愿
你自己的需求
去定义你认为合适的复制方式
而默认的复制构造函数
就是原样照应
有时候呢
我们不希望对象被复制构造
那这个时候
按照C++98标准的做法
我们就可以将复制构造函数
声明为私有的private类型
并且不提供函数体的实现
在C++11中有更好
更方便的办法
就是用delete关键字
来指示编译器
不生成默认的复制构造函数
现在我们看点类的定义
我们把它简化了
大家看
这里面只有一个构造函数
然后另外在复制构造函数之后
有一个等于delete
这个就是指示编译器
不要生成默认的复制构造函数