string 成员函数的模拟实现:
1.构造函数:
-
- string(const char* str = "")
- :_capacity(0)
- ,_size(0)
- ,_str(nullptr)
- {
- int len = strlen(str);
- _str = new char[len + 1];
- strcpy(_str, str);
- _size = _capacity = len;
- }
-
- void swap(string& s)
- {
- char* tmp = s._str;
- s._str = _str;
- _str = tmp;
-
- int tmp_int = s._size;
- s._size = _size;
- _size = tmp_int;
-
- tmp_int = s._capacity;
- s._capacity = _capacity;
- _capacity = tmp_int;
-
-
- }
- string(const string& s)
- {
- //法一
- /* int len = strlen(s._str);
- _str = new char[len + 1];
- strcpy(_str, s._str);
- _size = _capacity = len;*/
-
- //法二
- string tmp(s);
- swap(tmp);
-
-
- }
对于传字符串的构造函数,利用参数化列表初始化了成员函数,再在函数内实现空间开辟、字符串拷贝、成员赋值。
对于传同类对象的拷贝构造,一种方法是重新开辟空间,并把内容拷贝过去,实现深拷贝。一种方法是服用传字符串的构造函数,此时临时对象的内容就是所需要的内容,所以用一个swap函数将临时对象的成员内容和this指向的对象的成员内容互换,然后函数结束后对临时对象调用析构,处理了原本属于this指向的对象的内容的销毁。
2.赋值函数:
- string& operator=(const string& s)
- {
- //法一
- /* _size = s._size;
- _capacity = s._capacity;
-
- char* tmp = new char[_capacity+1];
- strcpy(tmp, s._str);
- delete[] _str;
- _str = tmp;
- return *this;*/
-
- //法二
- string tmp(s._str);
- swap(tmp);
- }
赋值函数的内容和传递对象的拷贝构造十分相似,都是将一个对象的内容深拷贝到另一个对象中,因此方法也有两种,原理和上述类似。
3.析构函数:
-
- ~string()
- {
- delete[]_str;
- _size = _capacity = 0;
-
-
- }
析构函数的主要作用就是销毁开辟的空间。
4.插入内容:
- void reverse(size_t n)
- {
- if (n > _capacity)
- {
- _capacity = n;
- char* tmp = new char[_capacity + 1];
- strcpy(tmp, _str);
- _str = tmp;
- }
-
- }
-
- void push_back(char c)
- {
- //扩容
- if (_size == _capacity)
- {
- reverse(2 * _capacity + 1);
- }
- _str[_size] = c;
- _str[++_size] = '\0';
-
- }
-
- string& operator+=(char c)
- {
- push_back(c);
- return *this;
- }
-
- void append(const char* str)
- {
- int len = strlen(str);
- if (_size+len> _capacity)
- {
- reverse(_size + len);
- }
- strcat(_str, str);
- _size += len;
-
- }
-
- string& operator+=(const char* str)
- {
- append(str);
- return *this;
- }
push_back()用于插入一个字符,append用于插入一个字符串,重载+=能实现插入一个字符或一个字符串,功能和push_back和append类似,因此直接复用,同时针对字符和字符串进行了函数重载。
5.insert和erase:
- // 在pos位置上插入字符c/字符串str,并返回该字符的位置
-
- string& insert(size_t pos, char c)
- {
- assert(pos <= _size);
- *this += c;
- for (int i = _size; i > pos; i--)
- {
- _str[i] = _str[i - 1];
- }
- _str[pos] = c;
- _str[_size] = '\0';
- return *this;
- }
-
- string& insert(size_t pos, const char* str)
- {
- assert(pos <= _size);
- int len = _size + strlen(str);
- int len1 = strlen(str);
- reverse(len);
- for (int i =_size; i >=(int)pos ; i--)
- {
- _str[i+len1] = _str[i];
- }
- for (int i = pos,j=0; i < pos+len1; i++)
- {
- _str[i] = str[j++];
- }
- _size = len;
- return *this;
- }
-
-
-
- // 删除pos位置上的元素,并返回该元素的下一个位置
-
- string& erase(size_t pos, size_t len=-1)
- {
- assert(pos <_size&&pos>=0);
- if (len == -1||pos+len>=_size)
- {
- _size = pos;
- _str[_size] = '\0';
- }
- else
- {
- for (int i = pos + len; i < _size; i++)
- {
- _str[i - len] = _str[i];
- }
- _size -= len;
- _str[_size] = '\0';
- }
-
- return *this;
- }
insert函数实现在pos位置插入一个字符或一个字符串,需要注意pos的类型是size_t,如果是头插用pos做循环条件可能会进行无限循环(即循环条件是大于或等于pos,此时pos等于0,但size_t类型永远大于或等于0)。
erase函数实现在pos位置删除len个字符,如果len忽略或者len过大,相当于删除pos位置往后所有字符,因此需要判断len。
6。流提取:
- istream& operator>>(istream& _cin, bit::string& s)
- {
- //充当临时缓冲区
- char tmp[101];
- s.clear();
- char ch;
- _cin.get(ch);
- int i = 0;
- while (ch != '\n' && ch != ' ')
- {
- if (i == 100)
- {
- tmp[i] = '\0';
- s += tmp;
- i = 0;
- }
- tmp[i++] = ch;
- _cin.get(ch);
- }
-
- if (i != 0)
- {
- tmp[i] = '\0';
- s += tmp;
- }
- return _cin;
- }
对于流提取,本函数的实现方式是一个字符一个字符的插入,如果直接放入this指向对象中,会存在多次扩容,效率低。因此使用了一个临时缓冲区,缓冲区未满的时候内容放入缓冲区中,满了以后把缓冲区的内容全放入this指向的对象中,再清空缓冲区。需要注意,在退出循环后缓冲区中仍可能存在内容未放入this指向对象中,因此还需要放入一次。