C++ 的浅拷贝和深拷贝(结构体)

转自 https://www.cnblogs.com/weekbo/p/8202754.html

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

假设存在一个结构体:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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” ,从而出错。而浅拷贝还存在着一个问题,因为一片空间被两个不同的子对象共享了,只要其中的一个子对象改变了其中的值,那另一个对象的值也跟着改变了。

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

#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指针地址不同,但是指向的内容一致,所以这拷贝成功。​

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

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

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

​建议:

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

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
结构体在C语言中是一种用户自定义的数据类型,它可以包含多个不同类型的变量。在进行拷贝操作时,可以使用深拷贝浅拷贝浅拷贝是指将一个结构体的成员值复制到另一个结构体中,如果结构体成员中有指针类型,则只是复制了指针的地址,而没有复制指针所指向的实际内容。这样,在修改其中一个结构体的成员时,可能会影响到另一个结构体深拷贝是指将一个结构体的成员值以及指针所指向的内容全部复制到另一个结构体中。这样,在修改其中一个结构体的成员时,不会影响到另一个结构体。 以下是一个示例代码,展示了如何进行深拷贝浅拷贝: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义一个结构体 typedef struct { int id; char name[20]; char* address; } Person; // 浅拷贝函数 void shallowCopy(Person* dest, Person* src) { *dest = *src; } // 深拷贝函数 void deepCopy(Person* dest, Person* src) { *dest = *src; dest->address = (char*)malloc(strlen(src->address) + 1); strcpy(dest->address, src->address); } int main() { // 原始结构体 Person person1; person1.id = 1; strcpy(person1.name, "John"); person1.address = "123 Main St"; // 浅拷贝 Person person2; shallowCopy(&person2, &person1); // 深拷贝 Person person3; deepCopy(&person3, &person1); // 修改person1的成员 person1.id = 2; strcpy(person1.name, "Mike"); person1.address = "456 Park Ave"; // 输出结果 printf("Person 1: %d %s %s\n", person1.id, person1.name, person1.address); printf("Person 2: %d %s %s\n", person2.id, person2.name, person2.address); printf("Person 3: %d %s %s\n", person3.id, person3.name, person3.address); // 释放内存 free(person3.address); return 0; } ``` 上述代码中,我们定义了一个Person结构体,包含id、name和address三个成员。然后我们实现了一个浅拷贝函数shallowCopy和一个深拷贝函数deepCopy,分别进行浅拷贝深拷贝操作。 在主函数中,我们创建了一个原始结构体person1,并将其分别进行了浅拷贝深拷贝到person2和person3中。接着修改了person1的成员,然后输出三个结构体的成员值,可以看到浅拷贝只复制了指针地址,而深拷贝复制了指针所指向的内容。 需要注意的是,对于深拷贝操作,我们还需要手动释放内存,以避免内存泄漏。在上述代码中,我们使用了malloc函数为address成员分配了内存,在释放person3时需要使用free函数释放该内存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值