深拷贝和浅拷贝是在C++中非常重要的概念,不管是在面试还是在未来的工作中都会经常用到,很多人在学完C++很长一段时间内可能都没有吃透什么是深拷贝和浅拷贝以及它们的运用,今天我们总结关于深浅拷贝的相关概念和底层原理:
一.浅拷贝:
首先我们来看一段代码:
#include <iostream>
#include <stdlib.h>
#include <string>
#include <assert.h>
using namespace std;
class String{
public:
String(const char* str = " ")
:_str(new char[strlen(str) + 1]){
strcpy_s(_str,(strlen(str)+1), str);
}
String(const String& p){
_str=p._str;
}
~String(){
if (_str){
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
int main(){
String* p = new String("hello world!!");
String* p2 = new String(*p);
delete p;
p = nullptr;
delete p2;
p2 = nullptr;
system("pause");
return 0;
}
这个代码的执行结果是有问题的————————
为什么会出现这样的程序奔溃现象????-----------------我们通过调试来进行解析:
我们发现p和p2的地址是相同的,即p和p2共用了一个相同的地址;
String类中char* 的私有变量和拷贝构造函数中的变量同时指向一块内存块,在释放时同一块内存被多次释放导致程序奔溃,这种方式就是浅拷贝;
浅拷贝也称位拷贝,编译器只是将对象中的值拷贝过来如果对象中管理资源,最后就会导致读个对象共用同一份资源,当其中一个对象销毁时就会将该资源释放掉,而此时其他的对象不知道资源已经被释放了以为还有效,所以当继续对资源进行操作时就会发生访问违规;要解决浅拷贝问题就要通过深拷贝;
二.深拷贝:
在以上代码的基础上,我们进行调整:
#include <iostream>
#include <stdlib.h>
#include <string>
#include <assert.h>
using namespace std;
class String{
public:
String(const char* str = " ")
:_str(new char[strlen(str) + 1]){
strcpy_s(_str,(strlen(str)+1), str);
}
String(const String& p)
:_str(new char [strlen(p._str)+1]){
strcpy_s(_str,(strlen(p._str)+1),p._str);
}
~String(){
if (_str){
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
int main(){
String* p = new String("hello world!!");
String* p2 = new String(*p);
delete p;
p = nullptr;
delete p2;
p2 = nullptr;
system("pause");
return 0;
}
此时程序正常运行:
我们来看p和p2的地址:
这就是深拷贝的过程,每个string类对象都要用空间来存放字符串,而p2要用p拷贝构造出来;
因此,深拷贝就是给每个对象独分配资源,保证多个对象不会因为资源共享,而造成资源多次释放,导致程序奔溃;
原理:拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样两个指针就指向了不同的内存位置。并且里面的内容是一样的,这样不但达到了我们想要的目的,还不会出现问题,两个指针先后去调用析构函数,分别释放自己所指向的位置。即为每次增加一个指针,便申请一块新的内存,并让这个指针指向新的内存,深拷贝情况下,不会出现重复释放同一块内存的错误;