一、std::string 的底层实现
1、深拷贝
1 class String{ 2 public: 3 String(const String &rhs):m_pstr(new char[strlen(rhs) + 1]()){ 4 } 5 private: 6 char* m_pstr; 7 };
这种实现方式,在需要对字符串进行频繁复制而又并不改变字符串内容时,效率比较低下。如果对一块空间只是进行读,就没必要采用深拷贝,当需要进行写的时候,再使用深拷贝申请新的空间
2、写时复制 (浅拷贝+引用计数)
当只是进行读操作时,就进行浅拷贝,如果需要进行写操作的时候,再进行深拷贝;再加一个引用计数,多个指针指向同一块空间,记录同一块空间的对象个数
- std::string之写时复制
当两个std::string发生复制或者赋值时,不会复制字符串内容,而是增加一个引用计数,然后字符串指针进行浅拷贝,其执行效率为O(1)。只有当修改其中一个字符串内容时,才执行真正的复制。
- 引用计数存在哪里?
堆区,为了好获取将将引用计数与数据放在一起,并且最好在数据前面,这样当数据变化的时候不会移动引用计数的位置
1 class String{ 2 public: 3 String():m_str(new char[5]() + 4){ 4 //new 5 表示4个字节存放引用计数,一个字节存放\0 +4 是为了将指针指向字符串位置 5 cout << "String()" << endl; 6 //引用计数初始化为1 (字符指针向前偏移,指向引用计数,并且转为int型指针,解引用得引用计数的值) 7 InitRefCount(); 8 } 9 10 String(const char* str):m_str(new char[strlen(str) + 5] + 4){ //有参构造 11 //申请空间大小,4B引用计数,还有\0 12 cout << "Sting(const char *)" << endl; 13 strcpy(m_str, str); 14 InitRefCount(); 15 } 16 17 String(const String &rhs):m_str(rhs.m_str){ //浅拷贝 18 cout << "String(const String &rhs)" << endl; 19 IncreaseRefCount(); 20 } 21 22 String &operator=(const String &rhs){ //赋值 23 if(this != &rhs){ 24 DecreaseRefCount(); 25 if(0 == getRefcount()){ //如果该空间引用计数为0,删掉该空间 26 delete [] (m_str - 4); 27 } 28 m_str = rhs.m_str; //浅拷贝,引用计数++ 29 IncreaseRefCount(); 30 } 31 return *this; 32 } 33 private: 34 //CharProxy中去重载=与<<运算符,争对读写有不同的操作 35 class CharProxy{ 36 public: 37 CharProxy(String &self, size_t idx):_self(self), _idx(idx){ 38 39 } 40 //写操作 41 char &operator=(const char &ch); //string是不完整类型,所以要在类外实现 42 //读操作 43 friend std::ostream &operator<<(std::ostream &os, const CharProxy &rhs); 44 45 private: 46 String &_self; //CharProxy在string里面写,是不完整类型,无法创建对象 47 size_t _idx; //self找到m_pstr,可以去下标,去除string中每一个字符 48 }; 49 50 public: 51 52 CharProxy operator[](size_t idx){ 53 return CharProxy(*this, idx); //临时对象,所以不能返回引用 54 } 55 56 #if 0 这样去重载[],无法区分读写操作 若对s1[0]进行读,依然会修改引用计数 57 char &operator[](size_t idx){ 58 if(idx < size()){ 59 if(getRefcount() > 1){ //共享空间,[idx]修改时进行深拷贝 60 char *tmp = new char[size() + 5]() + 4; //深拷贝 61 strcpy(tmp, m_str); 62 DecreaseRefCount(); 63 m_str = tmp; //浅拷贝 64 InitRefCount(); 65 } 66 return m_str[idx]; 67 }else{ 68 static char charNull = '\0'; 69 return charNull; 70 } 71 } 72 #endif 73 ~String(){ 74 75 DecreaseRefCount(); 76 if(0 == getRefcount()){ 77 78 delete [] (m_str - 4); 79 } 80 } 81 82 int getRefcount() const{ //获取引用计数 83 return *(int*)(m_str - 4); 84 } 85 86 const char* c_str() const{ 87 return m_str; 88 } 89 90 size_t size() const{ 91 return strlen(m_str); 92 } 93 94 friend std::ostream &operator<<(std::ostream &os, const String::CharProxy &rhs); 95 friend std::ostream &operator<<(std::ostream &os, const String &rhs); 96 private: 97 char *m_str; 98 /* static int refCount; //静态变量为所有对象所共享,无法表示不同对象的引用计数 */ 99 100 void InitRefCount(){ //初始化引用计数 101 *(int*)(m_str - 4) = 1; 102 } 103 104 void IncreaseRefCount(){ //引用计数++ 105 ++*(int*)(m_str - 4); 106 } 107 108 void DecreaseRefCount(){ //引用计数-- 109 --*(int*)(m_str - 4); 110 } 111 }; 112 113 114 std::ostream &operator<<(std::ostream &os, const String &rhs){ 115 if(rhs.m_str){ 116 os << rhs.m_str; 117 } 118 return os; 119 } 120 121 //写操作 122 char &String:: CharProxy::operator=(const char &ch){ 123 if(_idx < _self.size()){ 124 if(_self.getRefcount() > 1){ //共享空间,[idx]修改时进行深拷贝 125 char *tmp = new char[_self.size() + 5]() + 4; //深拷贝 126 strcpy(tmp, _self.m_str); 127 _self.DecreaseRefCount(); 128 _self.m_str = tmp; //浅拷贝 129 _self.InitRefCount(); 130 } 131 _self.m_str[_idx] = ch; //真正的写操作 132 return _self.m_str[_idx]; 133 }else{ 134 static char charNull = '\0'; 135 return charNull; 136 } 137 138 } 139 140 std::ostream &operator<<(std::ostream &os, const String::CharProxy &rhs){ 141 os << rhs._self.m_str[rhs._idx]; 142 return os; 143 }
测试代码:
1 void test(){ 2 3 String s1("hello"); 4 cout << "s1 = " << s1 << endl; 5 cout << "s1.getRefcount()" << s1.getRefcount() << endl; 6 printf("s1 address is %p\n", s1.c_str()); 7 8 cout << endl << endl; 9 String s2 = s1; 10 cout << "s1 = " << s1 << endl; 11 cout << "s2 = " << s2 << endl; 12 cout << "s1.getRefcount()" << s1.getRefcount() << endl; 13 cout << "s2.getRefcount()" << s2.getRefcount() << endl; 14 printf("s1 address is %p\n", s1.c_str()); 15 printf("s2 address is %p\n", s2.c_str()); 16 17 cout << endl << endl; 18 String s3("world"); 19 cout << "s3" << s3 << endl; 20 cout << "s3.getRefcount()" << s3.getRefcount() << endl; 21 printf("s3 address is %p\n", s3.c_str()); 22 23 cout << endl << endl; 24 s3 = s1; 25 cout << "s1 = " << s1 << endl; 26 cout << "s2 = " << s2 << endl; 27 cout << "s3 = " << s3 << endl; 28 cout << "s1.getRefcount()" << s1.getRefcount() << endl; 29 cout << "s2.getRefcount()" << s2.getRefcount() << endl; 30 cout << "s3.getRefcount()" << s3.getRefcount() << endl; 31 printf("s1 address is %p\n", s1.c_str()); 32 printf("s2 address is %p\n", s2.c_str()); 33 printf("s3 address is %p\n", s3.c_str()); 34 35 cout << endl << "对s3执行写操作" << endl; 36 //s3.operator[](idx) 37 //CharProxy = char; 38 s3[0] = 'H'; 39 cout << "s1 = " << s1 << endl; 40 cout << "s2 = " << s2 << endl; 41 cout << "s3 = " << s3 << endl; 42 cout << "s1.getRefcount()" << s1.getRefcount() << endl; 43 cout << "s2.getRefcount()" << s2.getRefcount() << endl; 44 cout << "s3.getRefcount()" << s3.getRefcount() << endl; 45 printf("s1 address is %p\n", s1.c_str()); 46 printf("s2 address is %p\n", s2.c_str()); 47 printf("s3 address is %p\n", s3.c_str()); 48 49 cout << endl << "对s1[0]执行读操作" << endl; 50 //cout << CharProxy 51 cout << "s1[0] = " << s1[0] << endl; 52 cout << "s1 = " << s1 << endl; 53 cout << "s2 = " << s2 << endl; 54 cout << "s3 = " << s3 << endl; 55 cout << "s1.getRefcount()" << s1.getRefcount() << endl; 56 cout << "s2.getRefcount()" << s2.getRefcount() << enl; 57 cout << "s3.getRefcount()" << s3.getRefcount() << endl; 58 printf("s1 address is %p\n", s1.c_str()); 59 printf("s2 address is %p\n", s2.c_str()); 60 printf("s3 address is %p\n", s3.c_str()); 61 }
3、短字符串优化
核心思想:发生拷贝时要复制一个指针,对小字符串来说,为啥不直接复制整个字符串呢,说不定还没有复制一个指针的代价大(小字符串复制指针,大字符串复制字符串)