刚看到一篇文章《 标准C++类std::string的内存共享和Copy-On-Write技术》,觉得很有意思,于是将个人的理解记录于此。
该文章的出处为:http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx
1.Copy-On-Write技术
Copy-On-Write技术使用了“引用计数”。当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加,当有类析构时,这个计数会减一,直到最后一个类析构时,此时的引用计数为1或是0,此时,程序才会真正的Free这块从堆上分配的内存。
所以,引用计数就是string类中写时才拷贝的原理。
由于string类的内存是在堆上动态分配的,所以既然共享内存的各个类指向的是同一个内存区,我们为什么不在这块区上多分配一点空间来存放这个引用计数呢?这样一来,所有共享一块内存区的类都有同样的一个引用计数,而这个变量的地址既然是在共享区上的,那么所有共享这块内存的类都可以访问到,也就知道这块内存的引用者有多少了。
于是,有了这样一个机制,每当我们为string分配内存时,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造可是赋值时,这个内存的值就会加一。而在内容修改时,string类为查看这个引用计数是否为0,如果不为零,表示有人在共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。
2.内存共享
在string类中,内存共享就是指多个string对象使用同一块内存,直到某一个string对象的内容发生改变时才为其分配内存。
3.这个两种技术的一个简单实现
下面是我实现的一个非常简单的string类MyString:
该类仅实现了:
a. MyString str1("good");//定义一个string对象
MyString str2;
b. MyString str3(str1);//拷贝构造
c. MyString str4;
str4 = str3;//对象赋值操作符
d. str4[0] = ''w;//[]操作符
e. str4.my_c_str();//获取C串
f. str4.my_size();//获取其大小
mystring.h
#ifndef _STRING_H_ #define _STRING_H_ class MyString { private : char *str; int size; public : MyString(const char *str = NULL); MyString(MyString& mystr); char* my_c_str(); int my_size(); char& operator[](const int i); MyString& operator=(MyString& mystr); ~MyString(); }; #endif
mystring.cpp
#include <iostream> #include <string> #include "mystring.h" using namespace std; int MyString::my_size() { return this->size; } char* MyString::my_c_str() { return this->str; } MyString::MyString(const char *str) { cout<<"construction..."<<endl; if(str != NULL) { this->size = strlen(str); this->str = new char[this->size + 1 + 1]; cout<<"size = "<<this->size<<endl; if(this->str != NULL) { strcpy(this->str, str); this->str[this->size + 1] = 0; printf("count = %d/n", this->str[this->size + 1]); } } else { this->str = NULL; this->size = 0; } } MyString::MyString(MyString& mystr) { cout<<"copy construction..."<<endl; if(this != &mystr) { this->str = mystr.my_c_str(); this->size = mystr.my_size(); cout<<"str = "<<this->str<<endl; this->str[this->size + 1]++; printf("count = %d/n", this->str[this->size + 1]); } } char& MyString::operator[](const int i) { cout<<"operator[]..."<<endl; if(i < 0 || i >= this->size || this->str == NULL) { static char nullchar = 0; return nullchar; } this->str[this->size + 1]--; printf("count = %d/n", this->str[this->size + 1]); char *temp = new char[this->size + 1 + 1]; strncpy(temp, this->str, this->size + 2); this->str = temp; cout<<"str = "<<this->str<<endl; this->str[this->size + 1] = 0; return this->str[i]; } MyString& MyString::operator=(MyString& mystr) { if(this == &mystr) { return *this; } else { if(this->str != NULL) { if(this->str[size + 1] > 0) { this->str[size + 1]--; } else { delete[] this->str; } } this->str = mystr.my_c_str(); this->size = mystr.my_size(); this->str[this->size + 1]++; printf("count = %d/n", this->str[this->size + 1]); return *this; } } MyString::~MyString() { if(this->size > 0) { printf("count = %d/n", this->str[this->size + 1]); if(0 == this->str[this->size + 1]) { cout<<"delete..."<<endl; delete[] this->str; } this->str[this->size + 1]--; } else { delete[] this->str; } }
4. 测试代码如下:
#include <iostream> #include <mcheck.h> #include "mystring.h" using namespace std; int main(void) { setenv("MALLOC_TRACE", "log", 1); mtrace(); MyString str1("lizuhong"); MyString str2(str1); MyString str3(str2); MyString str4(str3); MyString str5(str4); str2[0] = 'q'; str3[0] = 'w'; str4[0] = 's'; str5[0] = 'd'; MyString str6; str6 = str5; str6[0] = 'x'; return 0; }