String 类的实现(4)写时拷贝浅析

由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间。这种方法就是写时拷贝。这也是一种解决由于浅拷贝使多个对象共用一块内存地址,调用析构函数时导致一块内存被多次释放,导致程序奔溃的问题。这种方法同样需要用到引用计数:使用int *保存引用计数;采用所申请的4个字节空间。

 1 #include<iostream>
 2 #include<stdlib.h>
 3 using namespace std;
 4 class String
 5 {
 6 public:
 7     String(const char *pStr = "")
 8     {
 9         if (pStr == NULL)
10         {
11             _pStr = new char[1 + 4];
12             *((int*)pStr) = 1;
13             _pStr = (char*)(((int*)_pStr) + 1);
14             *_pStr = ‘\0‘;
15         }
16         else
17         {
18             _pStr = new char[my_strlen(pStr) + 1 + 4];
19             my_strcopy(_pStr, pStr);
20             *((int*)_pStr - 1) = 1;
21         }
22     }
23
24     String(const String& s)
25         :_pStr(s._pStr)
26     {
27         ++GetCount();
28     }
29
30     ~String()
31     {
32         Release();
33     }
34
35     String& operator=(const String& s)
36     {
37         if (this != &s)
38         {
39             Release();
40             _pStr = s._pStr;
41             --(GetCount());
42         }
43         return *this;
44     }
45
46     char& operator[](size_t index)//写时拷贝
47     {
48         if (GetCount() > 1)      //当引用次数大于1时新开辟内存空间
49         {
50             char* pTem = new char[my_strlen(_pStr) + 1 + 4];
51             my_strcopy(pTem + 4, _pStr);
52             --GetCount();       //原来得空间引用计数器减1
53             _pStr = pTem + 4;
54             GetCount() = 1;
55         }
56         return _pStr[index];
57     }
58     const char& operator[](size_t index)const
59     {
60         return _pStr[index];
61     }
62    friend ostream& operator<<(ostream& output, const String& s)
63    {
64         output << s._pStr;
65         return output;
66    }
67 private:
68     int& GetCount()
69     {
70         return *((int*)_pStr - 1);
71     }
72     void Release()
73     {
74         if (_pStr && (0 == --GetCount()))
75         {
76             _pStr = (char*)((int*)_pStr - 1);
77             delete _pStr;
78         }
79     }
80
81     char *_pStr;
82 };
83
84 int main()
85 {
86     String s1;
87     String s2 = "1234";
88     String s3(s2);
89     s2[0] = ‘5‘;
90     String s4;
91     s3 = s4;
92 } 

写时拷贝能减少不必要的内存操作,提高程序性能,但同时也是一把双刃剑,如果没按 stl 约定使用 String ,可能会导致极其严重的 bug ,而且通常是很隐蔽的,因为一般不会把注意力放到一个赋值语句。修改 String 数据时,先判断计数器是否为 0( 0 代表没有其他对象共享内存空间),为 0 则可以直接使用内存空间(如上例中的 s2 ),否则触发写时拷贝,计数 -1 ,拷贝一份数据出来修改,并且新的内存计数器置 0 ; string 对象析构时,如果计数器为 0 则释放内存空间,否则计数也要 -1 。

写时拷贝存在的线程安全问题

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。String类写时拷贝可能存在的问题详见:http://blog.csdn.net/haoel/article/details/24077

时间: 2024-10-23 03:11:54

String 类的实现(4)写时拷贝浅析的相关文章

C++ String类写时拷贝

    维基百科:     写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略.其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变.这过程对其他的调用者都是透明的(transparently).此作法主要的优点是如果调用者没有修

string类的写时拷贝

由于浅拷贝使多个对象共用一块内存地址,调用析构函数时导致一块内存被多次释放,导致程序奔溃. 实现string类的时候通常显示的定义拷贝构造函数和运算符重载函数. 由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝. 在构造函数中开辟新的空间时多开辟4个字节的空间,用来存放引用计数器,记录这快空间的引用次数. [cpp] view plain copy #inc

标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)

标准C++类std::string的内存共享,值得体会: 详见大牛:https://www.douban.com/group/topic/19621165/ 顾名思义,内存共享,就是两个乃至更多的对象,共同使用一块内存: 1.关于string的内存共享问题: 通常,string类中必有一个私有成员,其是一个char*,用户记录从堆上分配内存的地址,其在构造时分配内存,在析构时释放内存. 因为是从堆上分配内存,所以string类在维护这块内存上是格外小心的,string类在返回这块内存地址时,只返

简单的String类实现及写时拷贝

#include<iostream> using namespace std; class String { public: /*String(const char* str=" ") :_str(new char[strlen(str)+1]) { strcpy(_str, str); } */ String(const char* str = " ") { if (str == NULL) { _str = new char; _str[0] = '

string类的深浅拷贝,写时拷贝

浅拷贝:多个指针指向同一块空间,多次析构同一块内存空间,系统会崩溃.(浅拷贝就是值拷贝) 深拷贝:给指针开辟新的空间,把内容拷贝进去,每个指针都指向自己的内存空间,析构时不会内存崩溃. #include <iostream> #include <string> using namespace std; class String { public: String(const char*str) :_str(new char [strlen(str)+1]) { strcpy(_str

C++ String 写时拷贝

当类里面有指针对象时,采用简单的赋值浅拷贝,使得两个指针指向同一块内存,则析构两次,存在内存奔溃的问题,因此浅拷贝中利用引用计数. //引用计数浅拷贝 class String { public:  String(char*str = "")   :_str(new char[strlen(str) + 1])   , _pRefCount(new int(1))  {}  String(const String & s)   :_str(s._str)   , _pRefCo

String写时拷贝实现

头文件部分 1 /* 2 版权信息:狼 3 文件名称:String.h 4 文件标识: 5 摘 要:对于上版本简易的String进行优化跟进. 6 改进 7 1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需求大于定义大小时利用动态分配 8 2.实现大块内存的写时拷贝功能,提高效率,优化空间利用 9 3.类似new[]实现机制:将动态内存块大小信息保存为隐藏“头” 10 11 当前版本:1.2 12 修 改 者:狼 13 完成日期:2015-12-12 14 15 取代版本:1.

写时拷贝技术

Copy On Write(COW):写时拷贝技术 一.什么是写时拷贝技术: 写时拷贝技术可以理解为"写的时候才去分配空间",这实际上是一种拖延战术. 举个栗子: 二.写时拷贝技术原理: 写时拷贝技术是通过"引用计数"实现的,在分配空间的时候多分配4个字节,用来记录有多少个指针指向块空间,当有新的指针指向这块空间时,引用计数加一,当要释放这块空间时,引用计数减一(假装释放),直到引用计数减为0时才真的释放掉这块空间.当有的指针要改变这块空间的值时,再为这个指针分配自

写时拷贝 引用计数器模型

1.深浅拷贝的使用时机: 浅拷贝:对只读数据共用一份空间,且只释放一次空间: 深拷贝:数据的修改,的不同空间: 2.引用计数器模型 使用变量use_count,来记载初始化对象个数: (1).static模型(此处只用浅拷贝与浅赋值) #include<iostream> #include<string.h> #include<malloc.h> using namespace std; class String{ public:     String(const ch