深拷贝与浅拷贝
程序员文章站
2022-05-28 14:29:58
...
一 深浅拷贝概念
首先明白深浅拷贝是针对拷贝构造函数和赋值运算符重载而言的
所谓浅拷贝,就是由默认的拷贝构造函数所实现的对数据成员依次赋值,若类中含有指针类型的 数据,这种方式只是简单的把指针的指向赋值给新成员,但并没有给新成员分配内存导致两个成员指针指向同一块内存,要是再分别delete释放时就会出现问题,因此这种方式必然会导致错误,为了解决浅拷贝出现的错误,必须显示定义一个拷贝构造函数。使之不但复制数据成员,而且为对象分配各自的内存 空间,这就是所谓的深拷贝
二 浅拷贝
浅拷贝就是由默认的拷贝构造函数所实现的数据成员逐一赋值。通常默认的拷贝构造函数能够胜任这个工作,但若是类中含有指针类型的数据,这种数据成员逐一赋值的方式将产生错误。
以string为例讲解:我们先来看一下构造函数
上面这张图,我没有自己编写拷贝构造,因此用的是系统自动合成的,也就是说s2,s3指向同一块空间,调用两次析构函数,把堆上分配的空间释放了两次,因此出错
上面这张赋值运算符重载有两个错误:1.s1原来指向空间内存泄露 2.同一块空间被析构了两次
三 深拷贝
为了解决浅拷贝出现的错误,必须显示定义一个拷贝构造函数,使之不但能复制数据成员,而且为指针分配各自的动态内存
传统写法:老老实实开辟空间
具体实现代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class String
{
public:
/*String(char*str="")
:_str(new char[strlen(str)+1])
{
strcpy(_str, str);
}*/
String(char*str)
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String()
:_str(new char[1])//原因:1.这块必须分配一个空间,不然底下赋值运算符重载,释放_str之前就要判断是否为空 //2.因为析构用的是delete[],因此必须new[]
{
*_str = '\0';
}
char* Getstr()
{
return _str;
}
~String()
{
if (_str)
delete[]_str;
_str = NULL;
}
String ( const String& s)
{
_str = new char[strlen(s._str) + 1]; //1.分配空间拷贝
strcpy(_str, s._str); //2.拷贝
}
String& operator =(const String&s)
{
if (this != &s)
{
delete[]_str; // 1.释放s1原来指向的空间
_str = new char[strlen(s._str) + 1]; //2.分配一块与s2大小相同的空间
strcpy(_str, s._str); //3.拷贝
}
return *this;
}
private:
char* _str;
};
int main()
{
String s1("change world");
String s2("hello world");
String s3(s2);
s1 = s2;
cout << s2.Getstr() << endl;
cout << s3.Getstr() << endl;
cout << s1.Getstr() << endl;
system("pause");
return 0;
}
现代写法:让别人做,让构造函数做
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<math.h>
using namespace std;
class String
{
public:
/*String(char*str="")
:_str(new char[strlen(str)+1])
{
strcpy(_str, str);
}*/
String(char*str)
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String()
:_str(new char[1])//原因:1.这块必须分配一个空间,不然底下赋值运算符重载,释放_str之前就要判断是否为空 //2.因为析构用的是delete[],因此必须new[]
{
*_str = '\0';
}
char* Getstr()
{
return _str;
}
~String()
{
if (_str)
delete[]_str;
_str = NULL;
}
String ( const String& s)
:_str(NULL)
{
String tmp(s._str);
swap(_str, tmp._str);
}
String& operator =(const String&s)//赋值运算符重载2
{
if (this != &s)
{
String tmp(s);//调拷贝构造 或者调构造函数 String tmp(s._str)
swap(_str, tmp._str);
}
return *this;
}
String& operator=( String s)//赋值运算符重载3 参数不是引用,因此会创建一个形参,调拷贝构造
{
swap(_str, s._str);
return *this;
}
private:
char* _str;
};
int main()
{
String s1("change world");
String s2("hello world");
String s3(s2);
s1 = s2;
cout << s2.Getstr() << endl;
cout << s3.Getstr() << endl;
cout << s1.Getstr() << endl;
system("pause");
return 0;
}
深浅拷贝就讲到这儿。