深拷贝(深复制):被复制对象会把自己所有的非引用字段复制给新的对象相应的字段,同时也将在新的对象中开辟新的空间来存放被复制对象的引用字段指向的内容。
浅拷贝(浅复制):被复制对象的所有成员的值都和原来的对象的成员保持一致。
#include<iostream>
using namespace std;
class CT{
public: CT(int b)
{
a=b;
}
void Show()
{
cout<<a<<endl;
}
~CT()
{
}
private:
int a;
};
int main(){
CT A(10);
CT B=A;
B.Show();
return 0;
}
上面的代码是浅拷贝,因为默认的复制构造函数逐个复制非静态成员,复制提成员的值,并没有开辟新的空间。属于浅拷贝。
#include<iostream>
#include<string.h>
using namespace std;
class CA{
public:
CA()
{
a=4;
b=4;
str=new char[4];
strcpy(str,"C++");
}
void set(const char *s){
if (s!=0)
strcpy(str,s);
}
void Show()
{
cout<<str<<" " << b << endl;
}
~CA()
{
delete str;
}
private:
int a;
char *str;
public:
int b;
};
int main(){
CA A;
CA B=A;
B.Show();
B.set("CCC");
B.b = 10;
B.Show();
A.Show();
return 0;
}
在上面的代码中,在其构造函数上为成员变量分配了空间,但是却使用了默认的复制构造函数。
CA A;
CA B=A;
CA()
{
a=4;
str=new char[4];
strcpy(str,"C++");
}
即等效于:
CA B;
B.a = A.a;
B.str = A.str;
这样导致的问题是显而易见的,因为A和B的成员变量str相同,即他们指向同一个内存空间。这是一个很严重的问题,当其中任意的一个销毁,也就是意味着str指向的内存释放,而A和B终将会走向生命的尽头,也就意味着同一块内存被销毁两次,这就会带来意想不到的后果。这通常是内存管理不善的表现。
因此,在运行该代码时会出现下面的错误:
vincent@vincent:~/study/driver/copy$ ./CA2
<span style="color:#CC0000;">C++ 4
CCC 10
CCC 4</span>
*** glibc detected *** ./CA2: double free or corruption (fasttop): 0x0000000000a7a010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7f5f1ab66b96]
./CA2[0x400c5e]
./CA2[0x400af2]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7f5f1ab0976d]
./CA2[0x400989]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 2244375 /home/vincent/study/driver/copy/CA2
00601000-00602000 r--p 00001000 08:01 2244375 /home/vincent/study/driver/copy/CA2
00602000-00603000 rw-p 00002000 08:01 2244375 /home/vincent/study/driver/copy/CA2
00a7a000-00a9b000 rw-p 00000000 00:00 0 [heap]
7f5f1a7ec000-7f5f1a8e7000 r-xp 00000000 08:01 16646257 /lib/x86_64-linux-gnu/libm-2.15.so
7f5f1a8e7000-7f5f1aae6000 ---p 000fb000 08:01 16646257 /lib/x86_64-linux-gnu/libm-2.15.so
7f5f1aae6000-7f5f1aae7000 r--p 000fa000 08:01 16646257 /lib/x86_64-linux-gnu/libm-2.15.so
7f5f1aae7000-7f5f1aae8000 rw-p 000fb000 08:01 16646257 /lib/x86_64-linux-gnu/libm-2.15.so
7f5f1aae8000-7f5f1ac9d000 r-xp 00000000 08:01 16646823 /lib/x86_64-linux-gnu/libc-2.15.so
7f5f1ac9d000-7f5f1ae9d000 ---p 001b5000 08:01 16646823 /lib/x86_64-linux-gnu/libc-2.15.so
7f5f1ae9d000-7f5f1aea1000 r--p 001b5000 08:01 16646823 /lib/x86_64-linux-gnu/libc-2.15.so
7f5f1aea1000-7f5f1aea3000 rw-p 001b9000 08:01 16646823 /lib/x86_64-linux-gnu/libc-2.15.so
7f5f1aea3000-7f5f1aea8000 rw-p 00000000 00:00 0
7f5f1aea8000-7f5f1aebd000 r-xp 00000000 08:01 16649878 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f1aebd000-7f5f1b0bc000 ---p 00015000 08:01 16649878 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f1b0bc000-7f5f1b0bd000 r--p 00014000 08:01 16649878 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f1b0bd000-7f5f1b0be000 rw-p 00015000 08:01 16649878 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f1b0be000-7f5f1b1a3000 r-xp 00000000 08:01 6561616 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17
7f5f1b1a3000-7f5f1b3a2000 ---p 000e5000 08:01 6561616 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17
7f5f1b3a2000-7f5f1b3aa000 r--p 000e4000 08:01 6561616 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17
7f5f1b3aa000-7f5f1b3ac000 rw-p 000ec000 08:01 6561616 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17
7f5f1b3ac000-7f5f1b3c1000 rw-p 00000000 00:00 0
7f5f1b3c1000-7f5f1b3e3000 r-xp 00000000 08:01 16646249 /lib/x86_64-linux-gnu/ld-2.15.so
7f5f1b5bc000-7f5f1b5c1000 rw-p 00000000 00:00 0
7f5f1b5df000-7f5f1b5e3000 rw-p 00000000 00:00 0
7f5f1b5e3000-7f5f1b5e4000 r--p 00022000 08:01 16646249 /lib/x86_64-linux-gnu/ld-2.15.so
7f5f1b5e4000-7f5f1b5e6000 rw-p 00023000 08:01 16646249 /lib/x86_64-linux-gnu/ld-2.15.so
7fff10024000-7fff10045000 rw-p 00000000 00:00 0 [stack]
7fff10068000-7fff10069000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted
上面的内存错误就是由于在销毁时导致的。
浅拷贝 将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值(对应于变量b)。而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身(对应于变量str)。
深拷贝 同样,将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。
下面看一下深拷贝代码:
#include<iostream>
#include<string.h>
using namespace std;
class CA{
public:
CA(int len,char* cstr)
{
a=len;
b=len;
str=new char[len];
strcpy(str,cstr);
}
CA(const CA& C)
{
a=C.a;
str=new char[a];//深拷贝
if(str!=0)
strcpy(str,C.str);
}
void set(const char *s)
{
if (s!=0 && str!=0 && strlen(s)<a)
strcpy(str,s);
}
void Show()
{
cout<<str<<" "<<b<<endl;
}
~CA()
{
delete str;
}
private:
int a;
char *str;
public :
int b;
};
int main(){
CA A(10,"Hello!");
CA B=A;
A.Show();
B.Show();
B.set("CCCCCC");
B.b=8;
B.Show();
A.Show();
return 0;
}
结果如下:
Hello! 10 //A
Hello! 0 //B
CCCCCC 8 //B
Hello! 10 //A
深拷贝和浅拷贝的定义可以简单理解成:
如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。