一、String类实现
为了和库里面的string 区分开,使用命名空间delia将 string类和库里string隔离开
string类有3个成员变量:_str字符串内容、_size字符串大小、_capacity字符串容量
1. namespace delia 2. { 3. class string 4. { 5. private: 6. char* _str; 7. size_t _size; 8. size_t _capacity; 9. } 10. }
string的模拟实现包括:
分配空间时,要多分配1个字节的空间,这1个字节是留给'\0'的,_size最大为申请字节数-1,_capacity最大也为申请字节数-1
1. //构造函数 2. string(const char* str = "") 3. { 4. _str = new char[strlen(str) + 1]; 5. strcpy(_str, str); 6. 7. _size = strlen(str); 8. _capacity = _size; 9. }
使用库里的swap函数交换*this和s的内容:包括_str字符串内容、_size字符串大小和_capacity字符串容量
1. //实现交换函数 2. void swap(string& s) 3. { 4. //用::指定调用全局的swap函数即库里的swap函数 5. ::swap(_str, s._str); 6. ::swap(_size, s._size); 7. ::swap(_capacity, s._capacity); 8. }
(1)先使用s._str作为参数构造一个临时对象
(2)将临时对象tmp的内容和*this进行交换
注意:必须要在初始化列表进行初始化 ,否则交换完毕后tmp出了拷贝构造函数作用域会调用析构函数释放tmp在堆上申请的空间,因为如果_str没有被初始化为空指针,那么_str就是随机值,交换后,tmp对象的成员_str也是随机值,随机值的空间时不可以被释放的,会导致不可预知的错误;空指针时可以被释放的,因此_str必须被初始化为空指针。
1. //现代拷贝构造 2. string(const string& s) 3. :_str(nullptr) 4. , _size(0) 5. , _capacity(0) 6. { 7. string tmp(s._str); 8. swap(tmp); 9. }
使用swap()函数将*this的内容和s进行交换
1. //赋值运算符重载 2. string& operator=(string s) 3. { 4. swap(s); 5. return *this; 6. }
释放_str在对上申请的空间、将_size和_capacity置0
1. //析构函数 2. ~string() 3. { 4. delete[] _str; 5. _str = nullptr; 6. _size = 0; 7. _capacity = 0; 8. }
分两种:普通迭代器(可读可写)和const迭代器(只读)
(1)普通迭代器
1. //迭代器 2. iterator begin() 3. { 4. return _str; 5. } 6. 7. //迭代器 8. iterator end() 9. { 10. return _str + _size; 11. }
(2)const迭代器
1. //const迭代器 2. iterator begin() const 3. { 4. return _str; 5. } 6. 7. //const迭代器 8. iterator end() const 9. { 10. return _str + _size; 11. }
也分为普通operator[ ](可读可写)和const operator[ ](只读)
char要加&,如果不加,那么return就是传值返回,返回的是_str[i]的拷贝,即临时对象,而临时对象具有常性,不能被修改,只能读;如果要对_str进行修改的话,char要加&,用传引用返回
1. //const string对象遍历,只读 2. char& operator[](size_t i) const 3. { 4. assert(i < _size); 5. return _str[i]; 6. } 7. 8. //非const string对象遍历,可读可写 9. char& operator[](size_t i) 10. { 11. assert(i < _size); 12. return _str[i]; 13. }
1. //求string对象大小 2. size_t size() const 3. { 4. return strlen(_str); 5. }
获取c形式字符串,将 const string* 类型 转化为 const char* 类型
1. const char* c_str() 2. { 3. return _str; 4. }
开空间,扩展_capacity,_size不变
(1)申请新空间
(2)拷贝字符串
(3)释放旧空间
(4)指向新空间
(5)更新容量
1. void reserve(size_t n) 2. { 3. if (n > _capacity) 4. { 5. char* tmp = new char[n + 1];//1.申请新空间,多开的那一个空间,存放\0 6. strncpy(tmp, _str, _size + 1);//2.将字符串包含\0在内都拷贝到新空间 7. delete[] _str;//3.释放旧空间 8. 9. _str = tmp;//4.指向新空间 10. _capacity = n;//5.更新容量 11. } 12. }
开空间+初始化,扩展_capacity,_size也要修改
(1)当n<_size,无需增容
(2)当n>_size,分两种情况
①_size < n <_capacity,无需增容,直接将_size到n位置置为val
②n >_capacity,需增容,并将_size到n位置置为val
n>_size的这两种情况只有是否增容的区别,其他没有区别,可以合二为一
1. void resize(size_t n, char val = '\0')//如果没有显式给出val,val为\0 2. { 3. //1.当n<_size,无需增容 4. if (n < _size) 5. { 6. _size = n;//直接更新_size 7. _str[_size] = '\0';//将_size位置置为\0 8. } 9. //2.当n>_size,分两种情况 10. //(1)_size < n <_capacity,无需增容,直接将_size到n位置置为val 11. //(2)n >_capacity,需增容,并将_size到n位置置为val 12. //这两种情况只有是否增容的区别,其他没有区别 13. else 14. { 15. if (n > _capacity) 16. { 17. reserve(n); 18. } 19. 20. for (size_t i = _size; i < n; i++) 21. { 22. _str[i] = val; 23. } 24. 25. _str[n] = '\0';//将n的位置置\0 26. _size = n;//更新_size 27. } 28. }
要考虑是否需要开空间
1. //尾插一个字符 2. void push_back(char ch) 3. { 4. //字符个数已经达到容量时,重新开空间 5. if (_size == _capacity) 6. { 7. reserve(_capacity == 0 ? 4 : _capacity * 2);//如果是第一次开空间,就开4个字节,如果不是第一次开空间,就开成原来的2倍容量 8. } 9. 10. _str[_size] = ch;//将字符ch缀到字符串末尾 11. _str[_size + 1] = '\0';//字符ch后面加\0,表明字符串结束 12. _size++;//字符串大小+1 13. }
将str内容copy至this._str+_size(*this._str的末尾)位置
1. //追加字符串 2. void append(const char* str) 3. { 4. size_t len = _size + strlen(str);//函数执行完毕后字符串的大小 5. 6. if (len > _capacity) 7. { 8. reserve(len);//空间不够,增容 9. } 10. 11. strcpy(_str + _size, str);//将str内容拷贝至_str末尾 12. _size = len;//更新_size 13. }