关于浅拷贝和深拷贝这个问题遇上的次数不多,这次遇上整理一下,先说这样一个问题,关于浅拷贝的问题,先从最简单的说起。

假设存在一个结构体:

  

struct Student
{
string name;
int age;
};
  
int main()
{
struct Student stu = {"liming", 18};
struct Student stu2 = {"wanger", 20};
stu2 = stu; cout<<"age is : "<< stu2.age <<endl;
cout<<"name is :"<< stu2.name<<endl; }

  

​这样可以看到的结果是:

age is : 18

name is :liming

说明此时的拷贝是成功的,此时的结构体可以通过“=”来直接进行赋值操作,但是接下来的问题产生了,假设存在如下的结构体:​

  

struct stu
{
int i;
char c;
char* p;
}; int main()
{
struct stu s1,s2;
char * str = "rabbit is cute";
s1.i = 345;
s1.c = 'y';
s1.p = (char*)str;
s2 = s1;
printf("s2 %d, %c, %s\n", s2.i, s2.c, s1.p);
printf("s1 ptr: %d, s2 ptr : %d\n", s1.p, s2.p); }

  

​产生的结果是这样的:

s2 345, y, rabbit is cute

s1 ptr: 7934, s2 ptr : 7934

可以看到的是S2 确实得到了S1 传递的值,但是第二句的话却说明这样的一个问题,其实S2和S1的指针p都指向一个内存地址,这又说明了什么?

这说明指针的并没有将内容复制一块给新指针来指向,只是让新指针指向原来的那个内存,这样就相当于,指针在这个复制的过程中只是复制了地址,而不是内容。

原理:

在拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。但是注意缺省的构造函数却是这样的:缺省拷贝构造函数在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标--浅拷贝。

这就是产生问题的原因了,浅拷贝出现了。。。

 

用下图来解释这个问题:

在进行对象复制后,事实上s1、s2里的成员指针p都指向了一块内存空间(即内存空间共享了),在s1析构时,delete了成员指针p所指向的内存空间,而s2析构时同样指向(此时已变成野指针)并且要释放这片已经被s1析构函数释放的内存空间,这就让同样一片内存空间出现了“double free” ,从而出错。而浅拷贝还存在着一个问题,因为一片空间被两个不同的子对象共享了,只要其中的一个子对象改变了其中的值,那另一个对象的值也跟着改变了。

为了实现深拷贝,往往需要自己定义拷贝构造函数,在源代码里,我们加入自定义的拷贝构造函数如下:

在结构体中加入自己的拷贝构造函数:

struct stu
{
int i;
char c;
char* p;
stu operator=(stu& stuTmp)
{
i = stuTmp.i;
c = stuTmp.c;
p = new char(strlen(stuTmp.p) + 1);
strcpy(p, stuTmp.p);
return *this;
};
}; int main()
{
struct stu s1,s2;
char * str = "rabbit is cute";
s1.i = 345;
s1.c = 'y';
s1.p = (char*)str;
s2 = s1;
printf("s2 %d, %c, %s\n", s2.i, s2.c, s1.p);
printf("s1 ptr: %d, s2 ptr : %d\n", s1.p, s2.p); }

  

测试demo

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string> using namespace std; class stu
{
public:
int i;
char c;
char* p;
stu operator=(stu& stuTmp)
{
this->i = stuTmp.i;
this->c = stuTmp.c;
this->p = new char(strlen(stuTmp.p) + 1);
for (int i = 0; i < strlen(stuTmp.p); i++)
{
this->p[i] = stuTmp.p[i];
}
//strcpy(pp, stuTmp.p);
return *this;
};
}; int main()
{
struct stu s1, s2;
char * str = "rabbit is cute";
s1.i = 345;
s1.c = 'y';
s1.p = (char*)str;
s2 = s1;
printf("s2 %d, %c, %s\n", s2.i, s2.c, s1.p);
printf("s1 ptr: %d, s2 ptr : %d\n", s1.p, s2.p);
cin.get(); }

  

相当于重载operator=方法,这样还是运行,产生的结果就是这样的:

s2 345, y, rabbit is cute

s1 ptr: 7910, s2 ptr : 1050000

此时s1和s2中的指针p指向了不同的地址,可以打印一下此时这两个指针的内容是否一样,加入一下代码:

printf("s1 ptr: %s, s2 ptr : %s\n ", s1.p, s2.p);

产生的结果是:s1 ptr: rabbit is cute, s2 ptr : rabbit is cute

此时s1和s2中的p指针地址不同,但是指向的内容一致,所以这拷贝成功。​

其实类的结构和上面的结构体是类似的,其实可以将结构体看成一个类来处理,结构体也可有自己的构造、析构、重载运算符河函数,可以简单的认为结构体是类的一种形式。

拷贝有两种:深拷贝,浅拷贝 

当出现类的等号赋值时,会调用拷贝函数 在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。 但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。 所以,这时,必须采用深拷贝。 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。 简而言之,当数据成员中有指针时,必须要用深拷贝。​

​建议:

我们在定义类或者结构体,这些结构的时候,最后都重写拷贝构造函数,避免浅拷贝这类不易发现但后果严重的错误产生。​

C++ 的浅拷贝和深拷贝(结构体)的更多相关文章

  1. [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝

    一.结构体存储 #include<stdio.h> #include<stdlib.h> struct info{ char c; //1 2 4 8 double num; ...

  2. C语言中结构体的深拷贝和浅拷贝

    C++ 的浅拷贝和深拷贝(结构体) 拷贝有两种:深拷贝,浅拷贝 浅拷贝:拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标 (1)结构体中不存在指针成员变量时 typ ...

  3. PAT 甲级 1032 Sharing (25 分)(结构体模拟链表,结构体的赋值是深拷贝)

    1032 Sharing (25 分)   To store English words, one method is to use linked lists and store a word let ...

  4. C#浅拷贝与深拷贝区别

    也许会有人这样解释C# 中浅拷贝与深拷贝区别: 浅拷贝是对引用类型拷贝地址,对值类型直接进行拷贝. 不能说它完全错误,但至少还不够严谨.比如:string 类型咋说? 其实,我们可以通过实践来寻找答案 ...

  5. 17.结构体(typedef)

    1.结构体 a.结构体类型定义b.结构体变量定义c.结构体变量的初始化d.typedef改类型名e.点运算符和指针法操作结构体f.结构体也是一种数据类型,复合类型,自定义类型 2.结构体变量的定义 ( ...

  6. C语言复习:结构体

    结构体专题 01.结构体类型定义及结构体变量定义     char c1,char c2, char name[62]; int age     char name[62]; int age,char ...

  7. C# 之String以及浅拷贝与深拷贝

     一.String到底是值类型还是引用类型 MSDN 中明确指出 String 是引用类型而不是值类型,但 String 表面上用起来却像是值类型,这又是什么原因呢? 首先从下面这个例子入手: //值 ...

  8. ObjectiveC中的赋值,对象拷贝,浅拷贝与深拷贝

    在开发过程中我们经常会遇到对象拷贝的问题,下面我们分别讨论赋值操作.对象拷贝.以及浅拷贝(Shallow copy)与深拷贝(Deep copy)的区别与各自的实现方式. 一.不同对象的赋值操作 Ob ...

  9. C语言提高 (5) 第五天 结构体,结构体对齐 文件

    1昨日回顾 2作业讲解 3 结构体的基本定义 //1 struct teacher { int id; char name[64]; }; struct teacher t5 = { 5, " ...

随机推荐

  1. python-一个小练习

    myseq = '''[a:1,b:2,c:3] [a:3,b:3,c:8] [a:7,c:2:m:7,r:4] [a:2,c:4:m:6,r:4] [a:3,b:2,c:7,o:5]''' def ...

  2. scala快速学习笔记(三):Collections,包

    VI.Collections 1.Array 一些常用方法:println,  map( _ * 2), filter(_ % 2 == 0),  sum,   reserve Array是不可变的, ...

  3. Android Baseline小tip

    转载请注明出处:http://blog.csdn.net/bbld_/article/details/40709353 Baseline Alignment

  4. iOS app url scheme跳转到淘宝商品详情页 唤醒app

    最近涉及的一个业务,在app内的一个广告,点击打开webView,加载的是一个淘宝商品详情页,效果是打开该webView自动跳转至淘宝对应的页面,同时在自己的app仍然加载页面,点击评论等也同样能跳转 ...

  5. 在线安装Ganglia3.6.0,nginx+php搭建gweb,绝对通过

    环境:CentOS6.5 minimal 目标:安装Ganglia核心组件(gmond, gmetad, gmetric, gstat, libganglia).Ganglia web 准备 yum增 ...

  6. oracle问题系列 : ORA-02290: 违反检查约束条件

    报错如下: ### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-022 ...

  7. NEU 1683: H-Index

    题目描述 Given an array of citations (each citation is a non-negative integer) of a researcher, write a ...

  8. 织梦DedeCMS信息发布员发布文章默认自动审核更新并生成HTML页面

    织梦DedeCMS信息发布员发布文章默认自动审核更新并生成HTML页面 一直以为DEDECMS的信息发布员在后台发布文章后,非要管理员审核才能显示,今天一哥们问我这个问题.问:“能不能直接发布,并自动 ...

  9. JSON详解+ C# String.Format格式说明+ C# ListView用法详解 很完整

    JSON详解 C# String.Format格式说明 C# ListView用法详解 很完整

  10. Intel® Media SDK(一)

    A cross-platform API for developing media applications on Windows* Fast video playback, encode, proc ...