深浅拷贝和写时拷贝
来源:互联网 发布:网络暴力典型案例中国 编辑:程序博客网 时间:2024/05/13 04:37
拷贝:
事实是,在对象拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。
浅拷贝:
缺省拷贝构造函数在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标--浅拷贝。
浅拷贝的代码
class String{public: String(const char* str = "") :_str(new char[strlen(str) + 1]) { strcpy(_str, str); } //拷贝构造函数,它是构造函数的重载 String(const String& s) { //浅拷贝的情况下,只复制了指针本身,对指针所指向的内存并没有进行拷贝 _str = s._str; } String& operator=(const String& s) { if (this != &s) { _str = s._str; } return *this; } ~String() { if (_str) { //指针指向相同的内存空间,再次释放一块已经释放的空间将会出现错误 delete[] _str; } }private: char* _str;};
浅拷贝出现的问题:
(1)在进行对象复制后,事实上s1、s2里的成员指针m_psz都指向了一块内存空间(即内存空间共享了),在s1析构时,delete了成员指针m_psz所指向的内存空间,而s2析构时同样指向(此时已变成野指针)并且要释放这片已经被s1析构函数释放的内存空间,这就让同样一片内存空间出现了重复释放 ,从而出错。
(2)而浅拷贝还存在着一个问题,因为一片空间被两个不同的子对象共享了,只要其中的一个子对象改变了其中的值,那另一个对象的值也跟着改变了
深拷贝:
为每个对象都分配内存空间,防止析构时出现错误。
深拷贝的代码:
class String{public: String(const char* str = "") :_str(new char[strlen(str) + 1]) { strcpy(_str, str); } String(const String& s) { //内存的拷贝先要创建出一块内存 _str = new char[strlen(s._str)+1]; //然后将指针所指向的内容拷贝一份 strcpy(_str, s._str); } //赋值运算符的重载 String& operator=(const String& s) { //防止自身对自身赋值(因为会delete掉原来的内存空间,如果是自己给自己赋值,那么delete以后就找不到了) if (this != &s) { delete[] _str; _str = new char[strlen(s._str) + 1]; strcpy(_str, s._str); } return *this; } ~String() { if (_str) { delete[] _str; } }private: char* _str;};
深浅拷贝的不同:
深浅拷贝的主要区别在于是否共用空间。两者的代码只有拷贝构造函数和赋值运算符重载时,深拷贝需要重新开辟空间,而浅拷贝仅仅改变了指针的指向。
深拷贝的缺点:
重复的去开辟空间和释放空间效率是很低下的。
写时拷贝的引入:
大家都知道,重复的开辟空间和释放内存是很耗时的,效率也很低下,相对于写时拷贝,深拷贝就很耗费时间了,效率自然没有写时拷贝好。
写时拷贝是用一个计数器记住当前空间被多少个指针所指向,每次调用析构函数的时候,只需要看看自减后引用计数是否为零(是否只被最后一个指针所指向)。如果是,则销毁空间,如果不是,那么只需要引用计数减减即可,无需释放空间。
写时拷贝是一种高性能的写法,不过这种写法如果不注意就会把引用计数弄错,导致错误。所以在写时拷贝的写法的时候,一定要小心改写引用计数。
执行下面的一段代码,写时拷贝写法的指针应该如何指向呢?
void test(){ String s1("hello"); String s2(s1); String s3("world"); s1 = s3;}
图解上面的代码:
写时拷贝的代码:
class String{public: String(const char* str = "") :_str(new char[strlen(str)+1+4])//+1表示字符串后面要放一个'\0',+4表示多开辟一个空间存放引用计数 { _str += 4;//_str指向数据存放区 strcpy(_str, str); _GetCount() = 1;//刚开始这里写成了_GetCount++,其实是不对的,因为引用计数所指向的这a块空间并没有初始化,他的值并不是0,是一个随机值,随机值++还是随机值 } String(const String& s) :_str(s._str) { _GetCount()++; } String& operator=(String& s) { if (this != &s) { if (--_GetCount() == 0) { delete[] (_str-4); } ++s._GetCount(); _str = s._str; } return *this; } ~String() { if (--_GetCount() == 0) { delete[] (_str-4); } }public: int& _GetCount() { return *((int*)_str-1); }private: char* _str;};
阅读全文
0 0
- 深浅拷贝和写时拷贝
- C++中的深浅拷贝和写时拷贝
- c++深浅拷贝&写时拷贝实现
- 深浅拷贝与写时拷贝
- C++String深浅拷贝、写时拷贝
- 深浅拷贝&引用计数写时拷贝
- string的深浅拷贝以及写时拷贝问题
- 【c++】深浅拷贝,引用计数写时拷贝
- 深浅拷贝与写时拷贝的简单认知
- toString()和深浅拷贝
- 深浅拷贝
- 深浅拷贝
- 深浅拷贝
- 深浅拷贝
- 深浅拷贝
- 深浅拷贝
- 深浅拷贝
- 深浅拷贝
- Linux用户身份及切换
- 继承普通类
- JS中的事件:鼠标、键盘、HTML事件
- CDN缓存策略
- 搜索专题: HDU1258Sum It Up
- 深浅拷贝和写时拷贝
- hdu 1086 计算几何
- VS简易示波器(四):绘制波形
- I
- char类型的取值范围
- 前端基本开发环境与工具搭建
- [15]质量控制工具 规划质量工具-散点图
- 单链表
- js:快速学会应用正则表达式匹配字符