###== 浅谈c++ string底层实现方式==
c++底层实现采用了三种策略:
深拷贝Eager Copy
特点:当复制构造或者复制时,两个对象的指针不会指向同一块内存空间,会申请新的空间,并将数据惊醒复制。
缺点:在频繁复制而并不改变字符串内容时,效率低下,为了解决这总问题,采用写时复制的机制
写时复制 [COW] (Copy On Write)
特点:当两个string发生复制构造或者复制时,不会复制字符串的内容,两个对象的指针指向的是同一块内存空间,只有当一个字符串发生修改操作,才会执执行真正的复制。
实现策略:浅拷贝+引用计数
#include <iostream>
#include <cstring>
using std::cout;
using std::endl;
class String
{
public:
//refCount + value;
String()
:_pstr(new char[4 + 1]() + 4)//_pstr始终指向数据部分,新增四个字节时引用计数
{
cout << "String()" << endl;
*(int *)(_pstr - 4) = 1;
}
String(const char* pstr)
{
cout << "String(const char* pstr)" << endl;
if(pstr == nullptr)
{
_pstr = new char[5]() + 4; //指向数据部分
*(int *)(_pstr - 4) = 1;
return;
}
_pstr = new char[strlen(pstr) + 5]() + 4;
strcpy(_pstr,pstr);
*(int *)(_pstr - 4) = 1;
}
String(const String& rhs)
{
cout << "String(const String& rhs)" << endl;
//判断rhs._pstr是否是nullptr
if(rhs._pstr == nullptr)
{
_pstr = new char[5]() + 4; //指向数据部分
*(int *)(_pstr - 4) = 1;
return;
}
_pstr = rhs._pstr; //浅拷贝+refCount
++*(int *)(_pstr - 4);
}
//s2 = s1;
String& operator=(const String& rhs)
{
cout << "String& operator=(const String& rhs)" << endl;
//避免自复制
if(this == &rhs)
{
return *this;
}
//释放左操作数,引用计数-1,判断引用计数是否为0,为零则delete
--*(int *)(_pstr -4);
if(*(int *)(_pstr - 4) == 0)
{
delete[] (_pstr-4);
}
//浅拷贝
_pstr = rhs._pstr; //?
++*(int *)(_pstr - 4);
return *this;
}
char& operator[](size_t index)
{
if(index < 0 || index >= size())
{
std::cerr << "operator[index] 'index outOfRange or invalid_argument" << endl;
static char nullchar = '\0';
return nullchar;
}
//表明是共享的,执行写时复制
if(getRefCount() > 1)
{
char* ptmp = new char[size() + 5]() + 4;
strcpy(ptmp,_pstr);
--*(int *)(_pstr - 4);
_pstr = ptmp;
*(int *)(_pstr - 4) = 1;
}
return _pstr[index];
}
~String()
{
cout << "~String()" << endl;
--*(int *)(_pstr - 4);
if(*(int *)(_pstr - 4) == 0)
{
delete[] (_pstr - 4); //-4,不然会泄露四个字节
}
}
const char* c_str() const
{
return _pstr;
}
int getRefCount() const
{
return *(int *)(_pstr - 4);
}
size_t size() const
{
return strlen(_pstr);
}
friend std::ostream& operator <<(std::ostream& os, const String& rhs);
private:
char* _pstr;
};
std::ostream& operator <<(std::ostream& os, const String& rhs)
{
if(rhs._pstr)
{
os << rhs._pstr;
}
return os;
}
void test()
{
String s1("hello");
cout << "s1 = " << s1 << endl;
printf("s1's address:%p\n",s1.c_str());
cout << "s1.getRefCount() = " << s1.getRefCount() << endl;
cout << endl;
String s2 = s1;
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
printf("s1's address:%p\n",s1.c_str());
cout << "s1.getRefCount() = " << s1.getRefCount() << endl;
printf("s2's address:%p\n",s2.c_str());
cout << "s2.getRefCount() = " << s2.getRefCount() << endl;
cout << endl;
String s3("world");
cout << "s3 = " << s3 << endl;
printf("s3's address:%p\n",s3.c_str());
cout << "s3.getRefCount() = " << s3.getRefCount() << endl;
cout << endl;
s3 = s1;
printf("s1's address:%p\n",s1.c_str());
printf("s2's address:%p\n",s2.c_str());
printf("s3's address:%p\n",s3.c_str());
cout << "s1.getRefCount() = " << s1.getRefCount() << endl;
cout << "s2.getRefCount() = " << s2.getRefCount() << endl;
cout << "s3.getRefCount() = " << s3.getRefCount() << endl;
cout << endl << "对s3[0]执行写操作" << endl;
s3[0] = 'H';
s3[1] = 'Y';
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
cout << "s3 = " << s3 << endl;
printf("s1's address:%p\n",s1.c_str());
printf("s2's address:%p\n",s2.c_str());
printf("s3's address:%p\n",s3.c_str());
cout << "s1.getRefCount() = " << s1.getRefCount() << endl;
cout << "s2.getRefCount() = " << s2.getRefCount() << endl;
cout << "s3.getRefCount() = " << s3.getRefCount() << endl;
}
int main()
{
test();
return 0;
}
运行结果
短字符串优化[SSO] (Short string Optinization)
特点:当字符串小于16Byte时,在栈上申请空间存储字符串,当字符串长度>=16Byte,在堆上申请空间用于存储字符串。
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
//短字符串优化
void test()
{
int *pInt = new int(10);
string s1 = "hello";//当str长度小于16字节时,字符串存储再栈上
string s2 = "wuhanwangdaohub";//当字符串长度大于等于16字节时,字符串存储在堆上
string s3 = "welcometowuhanwangdao";
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
cout << "s3 = " << s3 << endl;
printf("s1'saddress : %p\n", s1.c_str());
printf("s2'saddress : %p\n", s2.c_str());
printf("s3'saddress : %p\n", s3.c_str());
printf("pInt's address : %p\n", &pInt);
printf("pInt's Pointer address : %p\n", pInt);
delete pInt;
}
int main(int argc, char **argv)
{
test();
return 0;
}