c++写时拷贝1

http://blog.csdn.net/SuLiJuan66/article/details/48882303

Copy On Write

Copy On Write(写时复制)使用了“引用计数”(reference counting),会有一个变量用于保存引用的数量。当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加,当有类析构时,这个计数会减一,直到最后一个类析构时,此时的引用计数为1或是0。此时,程序才会真正的Free这块从堆上分配的内存。

写时复制(Copy-On-Write)技术,就是编程界“懒惰行为”——拖延战术的产物。举个例子,比如我们有个程序要写文件,不断地根据网络传来的数据写,如果每一次fwrite或是fprintf都要进行一个磁盘的I/O操 作的话,都简直就是性能上巨大的损失,因此通常的做法是,每次写文件操作都写在特定大小的一块内存中(磁盘缓存),只有当我们关闭文件时,才写到磁盘上 (这就是为什么如果文件不关闭,所写的东西会丢失的原因)。

class String
{
public:String(char* ptr = "")           //构造函数:_ptr(new char[strlen(ptr)+1]){strcpy(_ptr, ptr);}String(const String& s):_ptr(new char[strlen(s._ptr)+1])//另外开辟空间{strcpy(_ptr, s._ptr);}~String(){if (_ptr){delete[] _ptr;}}
private:char* _ptr;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
void Test()
{String s1 = "hello world";int begin = GetTickCount();//记录此时毫秒数for (int i = 0; i < 10000; ++i){String s2 = s1;}int end = GetTickCount();//记录此时毫秒数cout << "cost time:" << end - begin << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  • GetTickCount : 在Release版本中,该函数从0开始计时,返回自设备启动后的毫秒数(不含系统暂停时间)。在头文件windows.h中。

  • 在上面for循环中,语句“String s2 = s1;”不断调用拷贝构造函数为s2开辟空间,执行完语句“String s2 = s1;”后,不断调用析构函数对s2进行释放,导致低效率,Test执行结果如下图:

    这里写图片描述



  • 写时拷贝~~写时拷贝~自然是我们自己想写的时候再进行拷贝(复制),下面引入几种方案如下:(试着判断哪一种方案可行)

这里写图片描述



  • 这里又引入另外一个概念“引用计数”:string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时(即其它对象也指向这块内存),这个计数为自动累加,上面方案中的_retCount就是用来计数的。

  • 简单地介绍一下上面三个方案。方案一和方案二是不可行的,方案一中的_retCount是属于每个对象内部的成员,当有多个对象同时指向同一块空间时,_retCount无法记录多个对象方案二中的_retCount是静态成员变量,是所有对象所共有,似乎可以记录,举个例子:对象s1、s2指向A空间,_retCount为2,对象s3、s4指向B空间,此时_retCount变为4,但是当想释放B空间时,应当在析构函数中_retCount减到0时释放,但是当_retCount减到0时,却发现释放的是A空间,而B空间发生了内存泄露。也就是静态成员变量_retCount只能记录一块空间的对象个数。

- 下面通过代码介绍方案三:

class String
{
public:String(char* ptr = "")        //构造函数:_ptr(new char[strlen(ptr)+1]), _retCount(new int(1))//每个对象对应一个整型空间存放{                          //指向这块空间的对象个数strcpy(_ptr, ptr);}String(const String& s)       //拷贝构造函数:_ptr(s._ptr), _retCount(s._retCount){_retCount[0]++;}String& operator= (const String& s)   //赋值运算符重载{if (this != &s){if (--_retCount[0] == 0){//旧的引用计数减1,如果是最后一个引用对象,则释放对象delete[] _ptr;delete[] _retCount;}_ptr = s._ptr;//改变this的指向,并增加引用计数_retCount = s._retCount;++_retCount[0];}return *this;}~String(){if (--_retCount[0] == 0){delete[] _ptr;delete[] _retCount;}}
private:char* _ptr;int* _retCount;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

  • 同样执行Test函数,测试结果如下图:

这里写图片描述



下面进一步优化方案三来介绍写时拷贝(写时复制)


方案三:是每个对象对应一个整型空间(即_refCount)存放指向这块空间的对象个数

再优化:不引用_refCount,但每次给_ptr开辟空间的时候,多开辟四个字节,用来记录指向此空间的对象个数,规定用开头那四个字节来计数。

class String
{
public:String(char* ptr = ""):_ptr(new char[strlen(ptr)+5]){_ptr += 4;strcpy(_ptr,ptr);_GetRefCount(_ptr) = 1;//每构造一个对象,头四个字节存放计数}String(const String& s):_ptr(s._ptr){_GetRefCount(_ptr)++;  //每增加一个对象,引用计数加1}String& operator= (const String& s){if (this != &s){Release(_ptr);_ptr = s._ptr;_GetRefCount(_ptr)++;}return *this;}char& operator [](size_t index){if (_GetRefCount(_ptr) > 1){--_GetRefCount(_ptr);//旧引用计数减1char* str = new char[strlen(_ptr) + 1];//另外开辟一个空间str += 4;strcpy(str, _ptr);_GetRefCount(str) = 1;_ptr = str;}}~String(){Release(_ptr);}inline void Release(char* ptr){if (--_GetRefCount(ptr) == 0){delete[](ptr - 4);}}inline int& _GetRefCount(char* ptr){return *(int*)(ptr - 4);//访问头四个字节}
private:char* _ptr;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

程序执行过程,看下图说话



这里写图片描述



这里写图片描述


对下列函数进行解析:

char& operator [](size_t index){if (_GetRefCount(_ptr) > 1){--_GetRefCount(_ptr);//旧引用计数减1char* str = new char[strlen(_ptr) + 1];//另外开辟一个空间str += 4;strcpy(str, _ptr);_GetRefCount(str) = 1;_ptr = str;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

当在主函数中执行语句:s1[0] = ‘w’;时,想要改变s1对象中_ptr[0]的值;但是当我们改变s1中_ptr[0]的值时,不希望把s2、s3中_ptr[0]的值也改变了。由于s1、s2、s3目前指向同一块空间,改变其中一个,另外两个肯定也跟着改变了,所以提供了另外一种方法:把对象s1分离出来,旧引用计数减1,另外给s1开辟一段跟原来一样的空间,存放一样的内容,这时候即使改变了s1的内容,也不影响s2、s3的对容。


一样看下图说话:


这里写图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383783.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java学习笔记十】输入输出流

在Java.io包中提供了一系列用于处理输入/输出的流类。从功能上分为两类&#xff1a;输入流和输出流。从六结构上可分为&#xff1a;字节流&#xff08;以字节为处理单位&#xff09;和字符流&#xff08;以字符为处理单位&#xff09;。 字符是由字节组成。在Java中所有字符用…

C++ 写时拷贝 2

什么情况下会用到c中的拷贝构造函数】&#xff1a; 1&#xff09;用已经存在的同类的对象去构造出另一个新的对象 2&#xff09;当函数的形参是类的对象时&#xff0c;这时调用此函数&#xff0c;使用的是值的拷贝&#xff0c;也会调用拷贝构造函数 3&#xff09;当函数的返…

【Java学习笔记十一】图形用户界面

图形用户界面或图形用户接口(Graphical User Interface&#xff0c;GUI)是指采用图形方式,借助菜单、按钮等标准界面元素&#xff0c;用户可以通过鼠标等外设向计算机系统发出指令、启动操作&#xff0c;并将系统运行的结果同样以图形方式显示给用户的技术。 GUI是事件驱动的&…

C++ 写时拷贝 3

http://blog.csdn.net/ljianhui/article/details/22895505 字符串一种在程序中经常要使用到的数据结构&#xff0c;然而在C中却没有字符串这种类型。在C中&#xff0c;为了方便字符串的使用&#xff0c;在STL中提供了一个string类。该类维护一个char指针&#xff0c;并封装和提…

C++ String类写时拷贝 4

http://blog.51cto.com/zgw285763054/1839752 维基百科&#xff1a; 写入时复制&#xff08;英语&#xff1a;Copy-on-write&#xff0c;简称COW&#xff09;是一种计算机程序设计领域的优化策略。其核心思想是&#xff0c;如果有多个调用者&#xff08;callers&#xff09;同时…

C++笔试复习

基础知识点 C中对象数组在定义的时候全部进行实例化&#xff08;与Java不同&#xff0c;Java相当于只是定义了一个指针数组&#xff0c;没有进行实例化&#xff09; 程序的三种基本控制结构是&#xff1a;顺序结构、循环结构、选择结构 一个C程序开发步骤通常包括&#xff1a…

Java笔试复习

Java程序运行 Java程序的执行必须经过编辑、编译和运行三个步骤 编辑指编写代码&#xff0c;最终形成后缀名为.java的Java源文件编译指使用Java编译器&#xff08;javac指令&#xff09;将源文件翻译为二进制代码&#xff0c;编译后生成后缀名为.class的字节码文件&#xff0c…

Java类名与包名不区分大小写

刚才写了一个简单的Java程序&#xff0c;经过测试得到一个令人震惊的结论&#xff1a;Java类名和包名是不区分大小写的 可以看一下这个例子&#xff1a; package Test;class aBcdEfG {}class AbCdefg {}public class TTT {public static void main(String[] args){AbCdefg tm…

处理大并发之一 对epoll的理解,epoll客户端服务端代码

http://blog.csdn.net/zhuxiaoping54532/article/details/56494313处理大并发之一对epoll的理解&#xff0c;epoll客户端服务端代码序言&#xff1a;该博客是一系列的博客&#xff0c;首先从最基础的epoll说起&#xff0c;然后研究libevent源码及使用方法&#xff0c;最后研究n…

链栈基本操作

http://blog.csdn.net/jwentao01/article/details/46765517###;栈基本概念&#xff1a; 栈&#xff08;stack&#xff09;是限定在表尾进行插入和删除操作的线性表&#xff08;或单链表&#xff09;。 //只能在一段进行插入和删除&#xff0c;因此不存在&#xff0c;在中间进行…

Linux网络编程---I/O复用模型之select

https://blog.csdn.net/men_wen/article/details/53456435Linux网络编程—I/O复用模型之select 1. IO复用模型 IO复用能够预先告知内核&#xff0c;一旦发现进程指定的一个或者多个IO条件就绪&#xff0c;它就通知进程。IO复用阻塞在select或poll系统调用上&#xff0c;而不是阻…

Linux网络编程---I/O复用模型之poll

https://blog.csdn.net/men_wen/article/details/53456474Linux网络编程—I/O复用模型之poll 1.函数poll poll系统调用和select类似&#xff0c;也是在指定时间内轮询一定数量的文件描述符&#xff0c;以测试其中是否有就绪者。 #include <poll.h>int poll(struct pollfd…

Linux网络编程---I/O复用模型之epoll

https://blog.csdn.net/men_wen/article/details/53456474 Linux网络编程—I/O复用模型之epoll 1. epoll模型简介 epoll是Linux多路服用IO接口select/poll的加强版&#xff0c;e对应的英文单词就是enhancement&#xff0c;中文翻译为增强&#xff0c;加强&#xff0c;提高&…

POJ 1741tree-点分治入门

学习了一下点分治&#xff0c;如果理解有误还请不吝赐教。 为了快速求得树上任意两点之间距离满足某种关系的点对数&#xff0c;我们需要用到这种算法。 点分治是树上的一种分治算法&#xff0c;依靠树和子树之间的关系进行分治从而降低复杂度。 和其他树上的算法有一些区别…

基于单链表的生产者消费者问题

『生产者与消费者问题分析』「原理」生产者生产产品&#xff0c;消费者消费产品。产品如果被消费者消费完了&#xff0c;同时生产者又没有生产出产品&#xff0c;消费者 就必须等待。同样的&#xff0c;如果生产者生产了产品&#xff0c;而消费者没有去消费&#x…

C++智能指针(二)模拟实现三种智能指针

https://blog.csdn.net/nou_camp/article/details/70186721在上一篇博客中提到了Auto_ptr(C智能指针&#xff08;一&#xff09;)&#xff0c;下面进行模拟实现Auto_ptr 采用类模板实现 #include<iostream> using namespace std; template<class T> class Autoptr …

C++智能指针(三)总结

https://blog.csdn.net/nou_camp/article/details/70195795 在上一篇博客中&#xff08;C智能指针&#xff08;二&#xff09;&#xff09;模拟实现了三种智能指针。 其中最好的就是shared_ptr,但是这并不代表它就是最完美的&#xff0c;它也有问题&#xff0c;这个问题就是循环…

c++11 你需要知道这些就够了

https://blog.csdn.net/tangliguantou/article/details/50549751c11新特性举着火把寻找电灯今天我就权当抛砖引玉&#xff0c;如有不解大家一起探讨。有部分内容是引用自互联网上的内容&#xff0c;如有问题请联系我。T&& 右值引用 std::move 右值引用出现之前我们只能…

c++仿函数 functor

https://www.cnblogs.com/decade-dnbc66/p/5347088.html内容整理自国外C教材先考虑一个简单的例子&#xff1a;假设有一个vector<string>&#xff0c;你的任务是统计长度小于5的string的个数&#xff0c;如果使用count_if函数的话&#xff0c;你的代码可能长成这样&#…

Ubuntu软件更新失败

刚安装好Ubuntu以后需要将系统的软件都更新一下&#xff0c;但是遇到一个问题就是下载仓库信息失败&#xff0c;大概是这个样子的错误&#xff1a; 经国遇到这样的问题可以试一下下面这个命令&#xff1a; sudo rm -rf /var/lib/apt/lists/* sudo apt-get update参考网址&…