C语言拾遗:位拷贝与值拷贝,浅拷贝与深拷贝

时间:2021-10-03 21:40:16

先从一个问题讲起


struct sct
{
int i;
double d;
};

结构体sct A与结构体sct B能构通过=号直接赋值吗?

即B = A有意义吗?

如果有意义,执行完语句后,是B只有A的地址,当A改变时B即改变,还是B具有独立的内存空间?

由此引出了几个概念,位拷贝与值拷贝,浅拷贝与深拷贝。

资料确实不好找,我先写下一些。

位拷贝与值拷贝待整理,是否浅拷贝就是位拷贝,深拷贝就是值拷贝,有待归纳。


浅拷贝就是把对象(结构)里的值完全复制给了另一个对象(结构)。如 A = B; 就是把B中的成员变量的值完全给A拷贝了一份。而由于是完全照搬的拷贝,它不够智能,所以对于地址也是完全照抄。如果B中有一个成员变量指针已经申请了内存那A中的那个同样的成员变量也指向同一个块内存不会在堆内再申请一块内存。这样的问题就是 当B把内存释放了那A内的指针就是野指针了(也就是说这个指针是乱指的,有可能就会因为访问到危险的区域而导致程序崩溃)。类的默认复制构造函数是将类中的非静态成员变量逐个复制,是浅拷贝。

深拷贝,这要求根据指针指向的值,重新申请内存,再依次将指针指向的值复制过来,有相互独立的内存空间。比如B中有int i = new int[30],要把B按深拷贝复制给A,则应该先在A中申请内存int i = new int[30],再用for循环或其它方法把内存里的值完全拷贝。这一般是需要你手动去做的。一般好的复制构造函数(要自己写)及某些函数(strcpy等),都是深拷贝。所以在自己写复制构造函数时,就应该写成深拷贝。


char * a = "windows";
char * b = "linux";
b = a;

如上代码,执行完后,b的值为"windows",但是相应的,b的地址其实与a相同。即b实际上是a的一个别名。这会产生很多你不希望产生的情况,比如a被删除或改动,则b也会改变。

可见深拷由与浅拷由主要是针对有指针的数据来说的,对于两个int类型的数据来说,不存在浅拷贝深拷贝之说。


实际上,ANSI C允许结构体赋值,而C++允许类对象赋值。而这里所说的赋值都是不够智能的浅拷贝。对于C++,使用默认的潜拷贝往往会造成一系列问题(参见C++ Primer Plus 类和动态内存分配部分),所以,一般可以如下重载赋值操作。

ClassName & ClassName::operator=(const ClassName & cls)
{
if (this == &cls)
return *this;
//删除使用new创建的数据
//根据cls重新new数据进行复制
return *this;
}


再回到题目来,可以看到结构体是可以直接通过=赋值的。

#include <cstdio>
#include <iostream>

using namespace std;

struct sct
{
int i;
double d;
char ch[6];
};

void show (sct &A, sct &B)
{
cout << "A的地址:" << &A << "\tA.i=" << A.i << "\tA.d=" << A.d << "\tA.ch=" << A.ch << endl;
cout << "B的地址:" << &B << "\tB.i=" << B.i << "\tB.d=" << B.d << "\tB.ch=" << B.ch <<endl;
}

int main ()
{
sct A = {5, 8.2, "hello"};
sct B = {0, 0.0, "nihao"};
cout << "struct sct" << endl << "{" << endl << "int i;\ndouble d;\nchar ch[6];\n" << "}" << endl;
cout << "原数据" << endl;
show(A, B);
B = A;
cout << "执行B = A" << endl;
show(A, B);
B.i += 10;
cout << "执行B.i += 10" << endl;
show(A, B);
system("pause");
}

C语言拾遗:位拷贝与值拷贝,浅拷贝与深拷贝

但是当结构体或类中存在指针时,就要注意了,可能导致两个结构体里的数据指针相同,当你对一个结构体里指针指向的数据进行修改,另一个结构体内指针指向的数据自然也修改了,这将导致你不想要的事情发生。而结构体中进行了动态内存分配时候,相同道理,也是你所不想的。

如下情况,则有浅拷贝与深拷贝之别:

C语言拾遗:位拷贝与值拷贝,浅拷贝与深拷贝

其中ch[6]的数据类型为char型数组,占用空间为1*6个字节,所以默认执行浅拷贝时,进行1*6个字节的完全拷贝,因而有自己独立的内存空间,值也得到拷贝。

而其中pch的数据类型为char型指针,它占用的空间为4*1个字节,所以默认执行浅拷贝时,进行4*1个字节的完全拷贝,因而其实得到拷贝的只是地址,当A.pch发生改变时,B.pch也会发生你所不想发生的情况。


主要参考资料:

http://blog.csdn.net/liam1122/article/details/1966617

http://wenku.baidu.com/link?url=PKgI5DMEobtUtLtN3kZclSwZnkdg-FqOKtjvGm6rZrkY9rb6NqGaNj3xy2aDM4MKvei3b2OCEgEqvgQPJdOAMrxRpitYiqXb0L2HuyDk8_q