【C++】浅拷贝和深拷贝(String类)
来源:互联网 发布:爱淘宝怎么推广 编辑:程序博客网 时间:2024/06/05 06:43
深拷贝与浅拷贝
简单的来说,【浅拷贝】是增加了一个指针,指向原来已经存在的内存。而【深拷贝】是增加了一个指针,并新开辟了一块空间
让指针指向这块新开辟的空间。
【浅拷贝】在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误
浅拷贝
为了形象化说明什么是深拷贝和浅拷贝,我们就先写一个String类
类里面包含【构造函数】,【拷贝构造函数】,【赋值运算符重载】,以及【析构函数】和【输出操作符“<<”的重载】
class String{public:String(const char *pStr = ""){if(NULL == pStr){pstr = new char[1];*pstr = '\0';}else{pstr = new char[strlen(pStr)+1];//加1,某位是'\0'strcpy(pstr,pStr);//用拷贝字符串的函数 }}String(const String &s):pstr(s.pstr)//浅拷贝的问题,指向同一块空间,可能造成释放的错误 ,这是浅拷贝的缺点 {}String& operator=(const String&s){if(this != &s){delete[] pstr;//将原来所指向的空间释放 pstr = s.pstr;//让pstr重新指向s的pstr所指向的空间(也会导致错误) } return *this;}~String(){if(NULL != pstr){delete[] pstr;//释放指针所指向的内容 pstr = NULL;//将指针置为空} } friend ostream&operator<<(ostream & _cout,const String &s){_cout<<s.pstr;return _cout;}private:char *pstr;};
经过测试之后,在某种情况下是可以正常运行的,在特定情况下是不可以正常的运行的
举一个不能正常运行的例子
int main(){String s1("sss");String s2(s1);String s3(NULL);s3 = s1;cout<<s1<<endl;cout<<s2<<endl;cout<<s3<<endl;return 0;}在该例子中,我们有三个String类的对象,s1调用【构造函数】存入字符"sss"
s2调用【拷贝构造函数】来利用s1进行初始化,s3则用【赋值运算符】来进行初始化
黑框框里输出了三个“sss”
然而!
这是为什么呢?
通过监视,我们发现他们指向的都是同一块空间,因为地址都是【0x01044570】
在让我们看看【析构函数】
~String(){if (pstr != NULL){delete[] pstr;pstr = NULL;}}当我们释放s3的时候,可以正常释放
然而当释放s2的时候,由于【s3已经释放过了】,所以s2所指向的这段空间已经不属于s1或者s2了
此时我们调用delete释放的时候,必然会崩溃(毕竟人家本来就不属于你呀)
深拷贝
深拷贝以及深浅拷贝的对比
深拷贝和浅拷贝的不同之处,仅仅在于修改了下【拷贝构造函数】,以及【赋值运算符的重载】
String(const String &s):pstr(new char[strlen(s.pstr)+1]){strcpy(pstr,s.pstr); }String& operator=(const String &s){if(this != &s){char* tmp = new char[strlen(s.pstr)+1];//动态开辟一个临时变量,然后将pstr指向这一个新的临时变量里delete[] pstr;//将原来的空间进行释放strcpy(tmp,s.pstr);//将s.pstr里的内容复制到临时变量中pstr = tmp;//pstr指向临时变量的这段空间}return *this;}
对比一下浅拷贝的【拷贝构造函数】和【赋值运算符重载】
String(const String &s):pstr(s.pstr)//浅拷贝的问题,指向同一块空间,可能造成释放的错误 ,这是浅拷贝的缺点 {}String& operator=(const String&s){if(this != &s){delete[] pstr;//将原来所指向的空间释放 pstr = s.pstr;//让pstr重新指向s的pstr所指向的空间(也会导致错误) } return *this;}
深拷贝完整版
class String{public:String(const char* pStr = ""){cout<<"String()"<<endl;if(NULL == pStr){pstr = new char[1];*pstr = '\0';}else{pstr = new char[strlen(pStr)+1];strcpy(pstr,pStr); }}String(const String &s):pstr(new char[strlen(s.pstr)+1]){strcpy(pstr,s.pstr); }String& operator=(const String &s){if(this != &s){char* tmp = new char[strlen(s.pstr)+1];//pstr;delete[] pstr;strcpy(tmp,s.pstr);pstr = tmp;}return *this;}~String(){if(NULL != pstr){delete[] pstr;pstr = NULL;}}private:char *pstr;};
除此之外,我们可以简化一下深拷贝的【拷贝构造函数】,【赋值运算符重载】
<span style="color:#000000;">String(const String& s):_ptr(NULL){String temp(s._ptr);std::swap(_ptr, temp._ptr);}</span>
在【拷贝构造函数】里用s定义临时变量,临时变量会自动调用构造函数开辟空间
然后用swap这个函数交换两个变量之间的内容
原来的内容在temp,并且出了【拷贝构造函数】就销毁了,避免了内存泄漏
String& operator=(const String& s){if (this != &s){String temp(s);swap(_ptr, temp._ptr);}return *this;}
String& operator=(const String& s){if(this != &s){String temp(s._ptr);swap(_ptr,temp._ptr);}return *this;}
String& operator=(String temp){swap(_ptr,temp._ptr);return *this;}在【赋值运算符重载】里,也可以用到类似的方法。在第三个方法里,直接传入一个临时变量,连if判断都可以省去了
总结
【浅拷贝】只是增加了一个指针,指向已存在对象的内存。
【深拷贝】是增加了一个指针,并新开辟了一块空间,让指针指向这块新开辟的空间。
【浅拷贝】在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误
- 【C++】浅拷贝和深拷贝(String类)
- String类 (浅拷贝/深拷贝/写时拷贝)
- 【C++】浅析浅拷贝,深拷贝及写时拷贝(copy_on_write),模拟实现String类。
- 【C++】c++String类浅拷贝、深拷贝
- 【C++】浅拷贝和深拷贝以及怎样实现一个简单的string类!!!
- String类的拷贝(浅拷贝,深拷贝,写时拷贝)
- String类的浅拷贝和深拷贝
- object c的浅拷贝(地址拷贝)和深拷贝(对象拷贝)
- String类的浅拷贝,深拷贝
- string类的浅拷贝,深拷贝,写实拷贝
- Object-c 深拷贝和浅拷贝
- [Objective-C]浅拷贝和深拷贝
- 【C++】深拷贝和浅拷贝
- 【C++】深拷贝和浅拷贝
- Objective-C浅拷贝和深拷贝
- 【C++】深拷贝和浅拷贝解析
- C中的深拷贝和浅拷贝
- [c++]深拷贝和浅拷贝
- 第十三天
- [leetcode]16. 3Sum Closest
- Escape from Enemy Territory(二分+bfs)
- [kuangbin带你飞]专题五 并查集 N POJ 1308
- Lightoj1014【基础题】
- 【C++】浅拷贝和深拷贝(String类)
- 选择合适的Hadoop数据类型
- Android中IntentService的原理及使用
- [JavaEE - JPA] 6. ORM的核心注解 - 基础类型以及嵌套类型
- Coupons and Discounts(细节题)
- Linux升级内核
- Host 'XXX' is not allowed to connect to this MySQL server 解决方案/如何开启MySQL的远程帐号
- BOOST_STATIC_ASSERT与assert的区别
- 洛谷 P1436 POJ 1191 [NOI1999 D1T2] 棋盘分割