还记得书本上的关于深拷贝和浅拷贝的问题吗?
如果自己写的一个类TestClass,采用的是默认的构造函数,或者采用自定义的构造函数(但是没有实现深拷贝)。那么下面的代码:
class TestClass
{
public:
char * p;
};
TestClass a;
TestClass b(a);
TestClass c = a;
上诉都是浅拷贝,也就是a.p == b.p == c.p;如果要想深拷贝,必须由你自己用代码实现。
STL中string的情况呢?
首先来看看下面语句的语义(也就是你这样写实际上是想要达到什么目的,或者是大家一看就知道的想要达到的目的):
string str1;
string str2(str1);
string str3 = str1;
上诉的三句实际上是想实现的什么呢?答案是深拷贝。实际上只要是采取上诉的赋值操作符或者是拷贝构造函数,用户都是想实现的是深拷贝。那么作为STL string的开发实现者就必须要实现string拷贝构造函数和=操作符的深拷贝。
但是这里string做了优化,做到对内存管理的优化。
这里不同版本的string采用了不同的方法:COW和SSO
看如下代码:
string str1("123456");
string str2(str1);
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str()); //1 step
str1[0] = '1';
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str()); //2 step
如果string采取的是COW的实现方式,那么1 step输出的str1.c_str()和str2.c_str()的值相等,说明str1和str2共享了字符串数组这部分的内存空间。(因为我的VS2010采用的是SSO方式,所以这里不能贴出实际的实验结果)。
但是当str1和str2中有任何一方的数据发生更改的时候,就会发生重新申请内存的操作和拷贝操作(因为两个string数据不一样了,不能再共享了)。
Copy On Write(COW)相当于是把申请内存和拷贝操作往后拖延了,直到不得不这样做时才这样做。这种方法是把导致分配支援的耗时操作往后延迟的方法。可能导致多个延迟操作在后面的某一时间同时需要统一完成(多个string数据同时发生改变),那么会导致后面的某个时间点会出现比较大的累计延迟。可以说有利也有弊。
再看看如下的代码:
string str1("123456789654464444444461322222222222222222222222222222222222222363333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222");
string str2(str1);
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
str1[0] = '1';
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
运行结果如下:
这是字符串比较长的时候的string申请内存空间的情况。很显然字符串数组(str1.c_str())的地址和string对象的地址差别较大,前者处于堆空间,后者处于函数的栈空间。这种情况一般就是我们所认识的深拷贝了,我们自己写的类的深拷贝也就是这种情况。
再看下面的代码:
string str1("123456");
string str2(str1);
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
str1[0] = '1';
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
运行结果是:
从结果可以看出&str1和str1.c_str()的值比较接近,他们都属于函数的栈空间。这就说明此时的string内部的字符串数组没有去堆上申请空间,而是足够小的字符串直接存在了对象本身的栈内存中。所以这种情况str1和str2是没有共享内存的,所有的数据都在各自的对象中。这在语义上是深拷贝,但是和我们自己实现的深拷贝不一样。这就是有的STL string版本实现的(small string optimization)SSO方式。VS2010就是采用的SSO方式。