一、默认拷贝构造函数

拷贝构造函数是一种特殊的构造函数(详情见:http://www.cnblogs.com/duwenxing/p/7429777.html),如果用户在定义类时没有显式地编写拷贝构造函数,那么C++编译器会在类中生成一个默认的拷贝构造函数。默认拷贝函数完成对象之间的位拷贝(即浅拷贝),换句话说就是用“旧对象”的成员对象对“新对象”的成员对象进行对应的一一赋值,如下:

 Student:Student(const Student &stu){
Name=stu.Name;
Age=stu.Age;
Gender=stu.Gender;
}

在编写自定义的类时,如果对象之间的拷贝仅仅要求完成对象之间的位拷贝,就像上例所示,那么编译器提供的默认拷贝构造函数就可以满足需求了,此时编程者可以不必再显式地在类中定义拷贝构造函数,以提高编程效率。

特别注意:如何禁止默认拷贝构造函数的调用

假设我们在定义类时不希望该类的默认拷贝构造函数被调用,即禁止用该类的一个对象通过简单的“值传递”方式去拷贝该类的另一个对象,那么我们可以在类中声明一个私有的拷贝构造函数(注意是声明,我们并不需要定义该函数)。由于该拷贝构造函数是私有的,则当用户试图调用默认拷贝构造函数时,系统会报出一个编译错误。

 #include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student()=default;
Student(string name){ //带参数的构造函数
Name=new string;//利用new为指针Name在内存中动态地分配空间
*Name=name;
}
~Student(){ //析构函数
if(Name!=NULL){
delete Name; //释放动态分配的空间
Name=NULL;
}
}
private:
string *Name;
19 Student(const Student& stu);//将拷贝构造函数声明为私有
}; int main(){
Student stu1("Tomwenxing");
24 Student stu2=stu1; //错误!
return ;
}

二、浅拷贝

浅拷贝,又称位拷贝,换句话说就是在进行对象拷贝时,只是用“旧对象”的成员对象对“新对象”的成员对象进行对应的一一赋值(就像默认拷贝构造函数做的那样)。如下图所示:

在大多数情况下,浅拷贝可以很好的完成工作。但如果类中含有动态成员或指针成员变量时,浅拷贝就会出现问题。例如:

 #include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student()=default;
Student(string name){ //带参数的构造函数
Name=new string;//利用new为指针Name在内存中动态地分配空间
*Name=name;
}
11 Student(const Student &stu){ //浅拷贝
12 Name=stu.Name;
13 }
~Student(){ //析构函数
if(Name!=NULL){
delete Name; //释放动态分配的空间
Name=NULL;
}
}
private:
string *Name;
}; int main(){
Student stu1("Tomwenxing");
Student stu2=stu1;
return ;
}

上面的例子在运行时会报出运行错误,我们在这里简单分析一下为什么:

当我们定义了对象stu1之后,系统中的内存情况大致如下:

此时对象stu1中的成员指针变量Name指向内存在堆为其分配的空间。当我们调用拷贝构造函数并利用对象stu1来创建对象stu2时,由于执行的是浅拷贝,只是将对象成员变量之间的值进行简单的拷贝赋值,此时对象stu1会将自己的成员变量指针Name的值拷贝赋值给对象stu2的成员指针变量Name,也就是说指针stu1.Name和指针stu2.Name此时指向的是堆中的同一个空间。如下图所示:

当程序执行完毕需要将对象stu1和对象stu2进行销毁时,两个对象的析构函数将会对同一个内存空间释放两次,从而导致运行错误。为了解决这种问题,在拷贝对象时就不能是简单的浅拷贝,而应该是深拷贝。

三、深拷贝

以上面的例子为例,深拷贝时不是简单的将对象stu1的指针变量Name赋值给对象stu2的指针变量Name,而是在堆中为指针stu2.Name分配内存空间,并将指针stu1.Name所指空间中存储的内容拷贝给指针stu2.Name所指的内存空间。如下:

 #include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student()=default;
Student(string name){ //带参数的构造函数
Name=new string;//利用new为指针Name在内存中动态地分配空间
*Name=name;
}
11 Student(const Student &stu){ //深拷贝
12 Name=new string;
13 *Name=*(stu.Name);
14 }
~Student(){ //析构函数
if(Name!=NULL){
delete Name; //释放动态分配的空间
Name=NULL;
}
}
private:
string *Name;
}; int main(){
Student stu1("Tomwenxing");
Student stu2=stu1;
return ;
}

上面的程序可以正确运行,在这里我们简单分析一下:

当我们定义了对象stu1之后,系统中的内存情况大致如下:

当对象stu2创建时,由于执行的是深拷贝,故会在堆中为对象stu2的指针Name重新分配空间,并将stu1.Name所指空间中存储的内容拷贝给stu2.Name所指的内存空间,此时系统中的内存情况大致如下:

此时指针stu1.Name和指针stu2.Name各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。

四、总结

假设一个类中拥有资源(堆或其他系统资源),当该类的一个对象对另一个对象进行拷贝时,系统对该类的资源进行了重新分配,那么这个过程就是深拷贝;反之若没有发生资源的重新分配而是简单的“值传递”,那么该过程就是浅拷贝。

C++:构造函数3——浅拷贝和深拷贝的更多相关文章

  1. c++中拷贝构造函数,浅拷贝和深拷贝的区别

    在C++提供了一种特殊的构造函数,称为拷贝构造函数.拷贝构造函数具有一般构造函数的所有特性,其作用是使用一个已经存在的对象(由拷贝构造函数的参数指定的对象)去初始化一个新的同类对象,即完成本类对象的复 ...

  2. 【C++札记】拷贝构造函数,浅拷贝和深拷贝

    一:拷贝构造函数 拷贝构造函数是一种特殊的构造函数,遵循如下的规则: 1.函数名和类名一致,没有返回值. 2.必须有一个参数,参数是本类型的一个引用变量. 3.拷贝构造函数可以访问参数对象的任意成员( ...

  3. C++拷贝构造函数:浅拷贝与深拷贝

    在介绍C++浅拷贝与深拷贝之前,我们先引出C++的拷贝构造函数. C++拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用.用于在建立一个新的对象时,使用一个已经存在的对象来初始化这个新对象.因 ...

  4. c++ 拷贝构造函数(重点在内含指针的浅拷贝和深拷贝)

    今天同事问了一个关于拷贝构造函数的问题,类中包含指针的情况,今天就来说说c++的拷贝构造函数. c++的拷贝构造函数是构造函数的一种,是对类对象的初始化,拷贝构造函数只有一个参数就是本类的引用. 注意 ...

  5. 关于JavaScript的浅拷贝和深拷贝

    在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...

  6. [转载]“浅拷贝”与“深拷贝”

    对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=88; int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量. C++中对象的复制就如同&quo ...

  7. C++ 浅拷贝与深拷贝探究

    C++浅拷贝与深拷贝探究 浅拷贝与深拷贝的概念是在类的复制/拷贝构造函数中出现的. 拷贝构造函数使用场景 对象作为参数,以值传递方式传入函数(要调用拷贝构造函数将实参拷贝给函数栈中的形参) 对象作为返 ...

  8. 浅拷贝和深拷贝(谈谈java中的clone)

    clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那么在java语言中,有 ...

  9. javascript篇-浅拷贝与深拷贝

    理解javascript 的浅拷贝与深拷贝,首先看一下js的数据类型: js有5种基本数据类型:undefined,null,boolean,number,string 还有一种复杂的数据类型(也叫引 ...

随机推荐

  1. 微信公众号开发 [03] 结合UEditor实现图文消息群发功能

    0.写在前面的话 如何实现微信平台后台管理中的,图文消息发送功能? 大概的过程如下: 通过类似表单的形式,将文章各部分内容提交到后台,封装成一个实体类,并持久化到数据库中 需要推送的时候,将不同的文章 ...

  2. 郑重推荐开源CANopen协议栈CANFestival(LGPL许可)!!!!!!!!

    郑重推荐开源CANopen协议栈CANFestival(LGPL许可)!!!!!!!!(这条文章已经被阅读了 次) 时间:2010/03/04 06:47am 来源:winshton [这个贴子最后由 ...

  3. 提取win10默认锁屏壁纸

    win10锁屏设置为windows聚焦时锁屏会有好看的图片出现.想让一张好看的图片一直使用,就去提取出来然后设置一下. 找到C盘:用户目录下 找到你的主机名文件夹: 在查看的选项栏中将隐藏文件夹显示: ...

  4. 基于jquery的插件turn.js学习笔记

    基于jquery的插件turn.js学习笔记 简介 turn.js是一个可以实现3d书籍展示效果的jq插件,使用html5和css3来执行效果.可以很好的适应于ios和安卓等触摸设备. How it ...

  5. ie中input光标问题

    为input 添加 readonly=”readonly”  UNSELECTABLE="on"   属性. IE中设置了readonly=”readonly”,点击使用日期选择器 ...

  6. 阿里云服务器+宝塔环境+LNMP+tp5.1安装和调试

    1.首先肯定得有个阿里云服务器,楼主是用来自己测试用得,所以就买了个1年得突发性实例,虽然网上都说别买这种,不过个人测试还是没有多少要求得 2.买了服务器之前可以顺带买个域名,再顺带去备案一下,阿里云 ...

  7. JavaWeb基础—dbutils的简单入门

    简明入门教程,参考:https://www.cnblogs.com/CQY1183344265/p/5854418.html 进行此章节之前,介绍一个JdbcUtils的再次的简单封装 (例如后面需要 ...

  8. JavaEE笔记(四)

    sql的完整顺序完整的sql语句,由6个子句组成1. from2. where3. group by4. select5. having6. order by # having 和 where的区别w ...

  9. 【转载】MSXML应用总结 概念篇

    原文:http://blog.sina.com.cn/s/blog_48f93b530100e9tr.html 微软提供了大量的XML开发工具和技术,而SMXML(Microsoft XML Core ...

  10. 09 ORM 多表操作,创建表,添加记录

    1.数据库表关系 1.一对多 为什么需要,重复字段太多 一对多关系表 Book id title price publish_id 1 python 100 1 2 php 200 2 3 go 10 ...