由深拷贝与浅拷贝引发的引用计数、写时拷贝技术
来源:互联网 发布:南京婚纱摄影软件 编辑:程序博客网 时间:2024/09/21 08:16
原文地址:http://blog.csdn.net/wjxxaut/article/details/52201576
一、理解深拷贝和浅拷贝:
- #include <iostream>
- using namespace std;
- class String
- {
- public:
- String(const char *str = "")
- {
- if(str == NULL)
- {
- data = new char[1];
- data[0] = '\0';
- }
- else
- {
- data = new char[strlen(str)+1];
- strcpy(data,str);
- }
- }
- ~String()
- {
- delete []data;
- data = NULL;
- }
- private:
- char *data;
- };
- int main()
- {
- String s1("hello");
- String s2 = s1;
- String s3;
- s3 = s1;
- return 0;
- }
但析构的时候,会先释放s2指向的空间,但当析构s1指向的空间时,因为s2和s1是指向相同空间的,s2已经将空间释放,s1就没有空间可以释放,所以s1的析构就导致了程序的非法访问,造成程序的崩溃。这种现象就叫做浅拷贝,即只拷贝指针。
- //重写拷贝构造函数:
- String(const String &s) //深拷贝
- {
- data = new char [strlen(s.data)+1];
- strcpy(data,s.data);
- }
- //重写赋值语句:
- String& operator=(const String &s) //深赋值
- {
- if(this != &s)
- {
- delete []data;
- data = new char[strlen(s.data)+1];
- strcpy(data,s.data)
- }
- return *this;
- }
深拷贝就是在拷贝的时候,将指针指向的空间也一同拷贝,这样,析构的时候,自己释放自己指向的空间就可以了。
二、理解深拷贝和浅拷贝各自的优缺点:
浅拷贝节省空间,相同的数据只保存一份,但因为多个指针指向同一个空间,会引发多次释放的问题;
深拷贝虽然每个指针会指向不同的空间,没有一个空间多次释放的问题,但可能保存的数据都是一样的,这样会导致空间的浪费。
三、使用引用计数解决浅拷贝实现中出现的问题:
所以只要能够解决浅拷贝中的同一个空间多次的释放的问题,当然是最好的!
这就引出了引用计数的方法:
当一个空间被一个指针指向时,计数为1,当每多一个指针指向时,计数加 1.
当析构时,释放一个指针对象,空间不释放,计数减 1,当计数为 0 时,释放空间
- #include <iostream>
- using namespace std;
- class String
- {
- public:
- String(const char *str = "")
- {
- if(str == NULL)
- {
- data = new char[1];
- data[0] = '\0';
- }
- else
- {
- data = new char[strlen(str)+1];
- strcpy(data,str);
- }
- ++use_count;
- }
- //重写拷贝构造函数:
- String(const String &s) //浅拷贝,引用计数加 1
- {
- data = s.data;
- ++use_count;
- }
- //重写赋值语句:
- String& operator=(const String &s) //浅赋值,引用计数加 1
- {
- if(this != &s)
- {
- data = s.data;
- ++use_count;
- }
- return *this;
- }
- ~String() //析构,引用计数减 1
- {
- if(--use_count == 0) //当引用计数为 0 时,释放空间
- {
- delete []data;
- data = NULL;
- }
- }
- private:
- char *data;
- static int use_count;
- };
- int String::use_count = 0;
- int main()
- {
- String s1("hello");
- String s2 = s1;
- return 0;
- }
运行上面的程序看着没有问题,可是,当我们再创建一个不同的对象时发现,不同的空间居然有相同的引用计数
String s3("world");
s3没有拷贝s1和s2,而是一个新的空间的指针对象,但我们发现还是相同的引用计数加 1,所以这样写的引用计数程序是有问题的。
注意:每个空间应该具有自己的引用计数,而不能所有空间共享一个引用计数。
四、解决引用计数中的写时拷贝技术实现
- //引用计数器类
- class String_rep
- {
- public:
- String_rep(const char *str):use_count(0)
- {
- if(str == NULL)
- {
- data = new char[1];
- data[0] = '\0';
- }
- else
- {
- data = new char[strlen(str)+1];
- strcpy(data,str);
- }
- }
- String_rep(const String_rep &rep):use_count(0)
- {
- data = new char[strlen(rep.data)+1];
- strcpy(data,rep.data);
- }
- String_rep& operatro=(const String_rep &rep)
- {
- if(this != &rep)
- {
- delete []data;
- data = new char[strlen(rep.data)+1];
- strcpy(data,rep.data);
- }
- return *this;
- }
- ~String_rep()
- {
- delete []data;
- data = NULL;
- }
- public:
- void increment()
- {
- ++use_count;
- }
- void decrement()
- {
- if(--use_count == 0)
- {
- delete this; //调动自身的析构函数
- }
- }
- private:
- char *data;
- int use_count;
- };
- class String
- {
- public:
- String(const char *str = "")
- {
- rep = new String_rep(str);
- rep->increment();
- }
- String(const String &s)
- {
- rep = s.rep;
- rep->increment();
- }
- ~String()
- {
- rep->decrement();
- }
- private:
- Stirng_rep *rep;
- };
- int main()
- {
- String s1("hello");
- String s2 = s1;
- String s3("world");
- return 0;
- }
一个String对象中只维护一个 指向String_rep类的rep指针:
s1 String_rep
[rep] ----- > data -------->[ h e l l o \0]
| use_count
| /
s2 /
[rep]_____/
创建s1对象,调用构造函数,指向一个String_rep对象,引用计数加 1
s1给s2初始化,调用拷贝构造函数,进行浅拷贝,s1和s2指向相同的String_rep对象,引用计数加 1,该对象的指针指向同一个空间
s3 String_rep
[rep]-------> data -------->[ w o r l d \0]
use_count
创建s3对象,调用构造函数,指向一个新的String_rep对象,引用计数加 1
赋值语句:
s3 = s2:
- String& operator=(const String &s) //赋值函数的编写要小心,只进行浅拷贝会发生内存泄漏
- {
- if(this != &s)
- {
- rep = s.rep;
- rep->increment();
- }
- return *this;
- }
赋值函数的编写要小心,只进行浅拷贝会发生内存泄漏,因为s3对象的rep指针原本指向的是String_rep对象,及String_rep
对象指针指向的空间,如果单纯将s2对象的rep值赋值给s3对象的rep值,则s3对象的rep指针指向的空间内存都会泄漏;
重写赋值语句:
- String& operator=(const String &s)
- {
- if(this != &s)
- {
- rep->cecrement(); //delete
- rep = s.rep; //new
- rep->increment(); //strcpy
- }
- return *this;
- }
将s3对象rep指针原先指向String_rep的引用计数减 1,再将s3的rep指针赋值为s2的rep指针,该String_rep对象的引用计数 加1
以上的浅拷贝的引用计数方式,解决了相同数据多份空间而造成浪费的问题,但如果我们更改任何一个空间的内容时,所有的拷贝都会发生更改,这是错误的,应该只更改自己的,不应该影响别的对象。
这就提出了写时拷贝技术,即只是拷贝时共享相同的空间,但当自己需要修改数据时,应该将数据拷贝出来,
然后改变自己的指向,即进行深拷贝。
//当需要修改时,在String类中的修改函数:
s2.to_upper();
- void to_upper()
- {
- if(rep->use_count > 1)
- {
- String_rep *new_rep = new String_rep(rep->data); //1.
- rep->decrement(); //2.
- rep = new_rep; //3.
- rep->increment();
- }
- char *ch = rep->data; //4.
- while(*ch != '\0')
- {
- *ch -= 32;
- ++ch;
- }
- }
当s2对象的rep指针指向的String_rep引用计数大于1时,修改时
1.用原来String_rep对象指针指向的数据创建一个新的String_rep对象;
2.将s2对象的rep指针指向的String_rep引用计数减 1;
3.将s2对象的rep指针指向新的String_rep对象,并将引用计数加 1
4.对s2对象的rep指针指向的新的String_rep对象指针指向的数据进行更改。
当s2对象的rep指针指向的String_rep引用计数等于1时,直接对进行更改
- 由深拷贝与浅拷贝引发的引用计数、写时拷贝技术
- 由深拷贝与浅拷贝引发的引用计数、写时拷贝技术
- C++::浅拷贝,深拷贝,引用计数的拷贝,写时拷贝
- String类的浅拷贝、深拷贝、引用计数、写时拷贝
- 浅拷贝、深拷贝与引用计数
- C++写时拷贝技术与引用计数
- 深浅拷贝&引用计数写时拷贝
- 写时拷贝与引用计数
- String类详解(浅拷贝,深拷贝,引用计数,写时拷贝)
- String类---深拷贝,简洁深拷贝,引用计数拷贝,写时拷贝
- 浅析深拷贝之写时拷贝&引用计数
- 引用计数+写时拷贝
- 引用计数写时拷贝
- 引用计数写时拷贝
- 引用计数写时拷贝
- 引用计数+写时拷贝
- 引用计数--写时拷贝
- 引用计数的写时拷贝
- ZooKeeper
- 使用IDEA搭建SSM框架
- NDK的使用
- 一个Spring Scheduler (Quartz) 动态添加,删除,修改任务的例子
- jupyter中sklearn、opencv无法导入,python中导入正常
- 由深拷贝与浅拷贝引发的引用计数、写时拷贝技术
- hue 大数据的web工具
- NOD1024 (大数据的处理)
- tcp
- 【1701H1】【穆晨】【171115】连续第三十六天总结
- JQuery各种用法参考
- 1.ES6简介
- Hibernate关联关系映射配置(一对多,多对多,一对一)
- c++11:lambda表达式