• C++:string类的常用接口说明及其模拟实现


    本文主要介绍string类和该类常用的接口,并根据接口功能对其进行模拟实现。

    目录

    一、string类的常用接口说明

    1. string类对象的常见构造

    2.string类对象的容量操作

    3.string类对象的访问及遍历操作

    4.string类对象的修改操作

    5. string类非成员函数

    二、string的模拟实现

    1.默认成员函数和成员变量

    浅拷贝与深拷贝

    2.迭代器相关函数的实现

    3.字符串大小与容量相关函数的实现

    4.字符串修改操作相关函数的实现

    5.字符串访问相关函数的实现

    6.字符串比较相关函数的实现

    7.流插入和流提取的实现

    8.完整代码


    使用string类时,必须包含#include头文件以及using namespace std;

    一、string类的常用接口说明

    (下面我们只介绍最常用的接口)

    1. string类对象的常见构造

    文档链接

    函数名称功能说明
    string();构造空的string类对象,即空字符串
    string (const string& str);用C-string来构造string类对象
    string (size_t n, char c);string类对象中包含n个字符c
    string (const string& str);拷贝构造函数

    演示:

    1. string(); //构造空的string类对象,即空字符串
    2. string (const string& str);//用C-string来构造string类对象
    3. string (size_t n, char c);//string类对象中包含n个字符c
    4. string(const string&s) //拷贝构造函数

    1. void StringTest()
    2. {
    3. string s1; // 构造空的string类对象s1
    4. string s2("hello world"); // 用C格式字符串构造string类对象s2
    5. string s3(6,'x');//有6个'x'字符的字符串构造string类对象s3
    6. string s4(s2); // 拷贝构造s4
    7. }

    2.string类对象的容量操作

    函数名称功能说明
    size返回字符串有效字符长度
    length返回字符串有效字符长度
    capacity返回空间总大小
    empty检测字符串释放为空串,是返回true,否则返回false
    clear清空有效字符
    reserve为字符串预留空间
    resize将有效字符的个数该成n个,多出的空间用字符c填充

    1.size 和 length:返回字符串有效字符长度

    1. size_t size() const;
    2. size_t length() const;
    1. int main()
    2. {
    3. string s1("hello world");
    4. cout << s1.size() << endl;//11
    5. cout << s1.length() << endl;//11
    6. return 0;
    7. }

    size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。

    2.capacity:返回空间总大小

    size_t capacity() const;
    1. int main()
    2. {
    3. string s1("hello world");
    4. string s2("C++");
    5. cout << s1.capacity() << endl;//15
    6. cout << s2.capacity() << endl;//15
    7. return 0;
    8. }

    返回当前对象中该字符串所分配的存储空间的大小。

    3.empty:检测字符串释放为空串,是返回true,否则返回false

    bool empty() const;
    1. int main()
    2. {
    3. string s1("hello world");
    4. string s2;
    5. if (s1.empty())
    6. cout << "s1 is empty" << endl;
    7. if (s2.empty())
    8. cout << "s2 is empty" << endl;
    9. return 0;
    10. }

    输出:s2 is empty

    4.clear:清空有效字符

    void clear();
    1. int main()
    2. {
    3. string s("hello world");
    4. cout << s.size() << endl;// 11
    5. cout << s.length() << endl;// 11
    6. cout << s.capacity() << endl;// 15
    7. cout << s << endl;// hello world
    8. s.clear();
    9. cout << s.size() << endl;// 0
    10. cout << s.capacity() << endl;// 15
    11. return 0;
    12. }

    该函数将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小

    5.reserve:为字符串预留空间

    void reserve (size_type n = 0);
    1. int main()
    2. {
    3. string s("hello world");
    4. cout << s.size() << endl;// 11
    5. cout << s.capacity() << endl;// 15
    6. s.reserve(100);
    7. cout << s.size() << endl;// 11
    8. cout << s.capacity() << endl;// 111
    9. s.reserve(50);
    10. cout << s.size() << endl;// 11
    11. cout << s.capacity() << endl;// 111
    12. }

    为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。

    如果提前已经知道string中大概要放多少个元素,可以提前将string中空间设置好,避免频繁扩容。

    6.resize:将有效字符的个数该成n个,多出的空间用字符c填充

    1. void resize (size_t n);
    2. void resize (size_t n, char c);
    1. int main()
    2. {
    3. string s;//空字符串
    4. // 将s中有效字符个数增加到10个,多出位置用'a'进行填充
    5. s.resize(10, 'a');
    6. cout << s.size() << endl; // 10
    7. cout << s.capacity() << endl;// 15
    8. cout << s << endl; // “aaaaaaaaaa”
    9. // 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
    10. // 注意此时s中有效字符个数已经增加到15个
    11. s.resize(15);
    12. cout << s.size() << endl;// 15
    13. cout << s.capacity() << endl;// 15
    14. cout << s << endl; // "aaaaaaaaaa\0\0\0\0\0"
    15. // 将s中有效字符个数缩小到5个
    16. s.resize(5);
    17. cout << s.size() << endl;// 5
    18. cout << s.capacity() << endl;// 15
    19. cout << s << endl; // "aaaaa"
    20. return 0;
    21. }

    resize(size_t n) resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用 来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。

    注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

    3.string类对象的访问及遍历操作

    函数名称功能说明
    operator[]返回pos位置的字符,const string类对象调用
    begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
    rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的逆向迭代器
    范围forC++11支持更简洁的范围for的新遍历方式

    1.operator[]:返回pos位置的字符,const string类对象调用

    1. char& operator[] (size_t pos);
    2. const char& operator[] (size_t pos) const;
    1. int main()
    2. {
    3. string s1("hello world");
    4. const string s2("Hello world");
    5. cout << s1 << " " << s2 << endl;//hello world Hello world
    6. cout << s1[0] << " " << s2[0] << endl;//h H
    7. s1[0] = 'H';
    8. cout << s1 << endl;//Hello world
    9. // s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
    10. }

    2.string类对象的遍历操作

    3种遍历方式:①for+operator[]   ②迭代器    ③范围for

    1. iterator begin();
    2. const_iterator begin() const;
    3. iterator end();
    4. const_iterator end() const;
    5. reverse_iterator rbegin();
    6. const_reverse_iterator rbegin() const;
    7. reverse_iterator rend();
    8. const_reverse_iterator rend() const;
    1. int main()
    2. {
    3. string s("hello world");
    4. // 3种遍历方式:
    5. // 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
    6. // 另外以下三种方式对于string而言,第一种使用最多
    7. // 1. for+operator[]
    8. for (size_t i = 0; i < s.size(); ++i)
    9. cout << s[i];
    10. //hello world
    11. cout << endl;
    12. // 2.迭代器
    13. string::iterator it = s.begin();
    14. while (it != s.end())
    15. {
    16. cout << *it;
    17. ++it;
    18. }
    19. //hello world
    20. cout << endl;
    21. // string::reverse_iterator rit = s.rbegin();
    22. // C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
    23. // 逆置打印
    24. auto rit = s.rbegin();
    25. while (rit != s.rend())
    26. {
    27. cout << *rit;
    28. ++rit;
    29. }
    30. //dlrow olleh
    31. cout << endl;
    32. // 3.范围for
    33. for (auto ch : s)
    34. cout << ch;
    35. //hello world
    36. return 0;
    37. }

    4.string类对象的修改操作

    函数名称功能说明
    push_back在字符串后尾插字符c
    append在字符串后追加一个字符串
    insert在字符串pos位置插入一个字符或字符串
    erase删除字符串pos位置之后的len个字符
    operator+=在字符串后追加字符串str
    c_str返回C格式字符串
    find 从字符串中pos位置从前向后找字符c,返回该字符在字符串中的位置
    rfind从字符串中pos位置从后向前找字符c,返回该字符在字符串中的位置
    substr在str中从pos位置开始,截取n个字符,然后将其返回

    1.push_back:在字符串后尾插字符c

    void push_back (char c);
    1. string s1("hello");
    2. s1.push_back('!');
    3. cout << s1 << endl; //hello!

    2.append:在字符串后追加一个字符串

    1. string& append (const string& str);//追加str
    2. string& append (const string& str, size_t subpos, size_t sublen);
    3. //追加str从subpos位置开始的向后sublen个字符
    4. string& append (const char* s);//追加字符串s
    5. string& append (const char* s, size_t n);//追加字符串s的n个字符
    6. string& append (size_t n, char c);//追加n个c字符
    1. string s1("hello");
    2. string s2(" world");
    3. s1.append(s2); //hello world
    4. s1.append(s2, 1, 5);//hello worldworld
    5. s1.append("!!!");//hello worldworld!!!
    6. s1.append("12345", 3);//hello worldworld!!!123
    7. s2.append(3, '!');// world!!!

    3.insert:在字符串pos位置插入一个字符或字符串

    1. string& insert (size_t pos, const string& str);
    2. //在pos位置插入str
    3. string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
    4. //在pos位置插入str的从subpos位置开始的sublen个字符
    5. string& insert (size_t pos, const char* s);
    6. //在pos位置插入字符串s
    7. string& insert (size_t pos, const char* s, size_t n);
    8. //在pos位置插入字符串s的n个字符
    9. string& insert (size_t pos, size_t n, char c);
    10. //在pos位置插入n个字符c
    1. string s1("012345");
    2. string s2("xxabxxcd");
    3. string s3("**");
    4. s1.insert(1,s3);//0**12345
    5. s1.insert(3, s2, 0,2);//0**xx12345
    6. s3.insert(2,"11");//**11
    7. s3.insert(4, "2234", 3);//**11223
    8. s3.insert(1, 3, 'x'); //*xxx*11223

    4.operator+=:在字符串后追加字符串str

    1. string& operator+= (const string& str);
    2. string& operator+= (const char* s);
    3. string& operator+= (char c);
    1. string s1("hello");
    2. string s2("world");
    3. s1 += s2;//helloworld
    4. s1 += "123"; //helloworld123
    5. s1 += '!'; //helloworld123!

    在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

    5.c_str:返回C格式字符串

    const char* c_str() const;
    1. string s1("hello world");
    2. const char* pstr = s1.c_str();
    3. cout << pstr << endl;//hello world

    返回一个指向数组的指针,该数组包含一个以null结尾的字符序列(即C字符串),表示字符串对象的当前值。

    6.在str中从pos位置开始,截取n个字符,然后将其返回

    string substr (size_t pos = 0, size_t len = npos) const;

    npos是string里面的一个静态成员变量

    static const size_t npos = -1;为size_t类型的最大值,一般用于表示到字符串的末尾。

    1. string s1("hello world");
    2. string s2 = s1.substr();
    3. cout << s2 << endl;//hello world

    7.find,rfind:从字符串中pos位置从前(后)向后(前)找字符c,返回该字符在字符串中的位置。

    1. size_t find (const string& str, size_t pos = 0) const;
    2. size_t find (const char* s, size_t pos = 0) const;
    3. size_t find (char c, size_t pos = 0) const;
    4. size_t rfind (const string& str, size_t pos = npos) const;
    5. size_t rfind (const char* s, size_t pos = npos) const;
    6. size_t rfind (char c, size_t pos = npos) const;
    1. string s1("hello,world!!");
    2. string s2("world");
    3. cout << s1.find(s2) << endl;//6
    4. cout << s1.find("world") << endl;//6
    5. cout << s1.find('!') << endl;//11
    6. cout << s1.rfind(s2) << endl;//6
    7. cout << s1.rfind("world") << endl;//6
    8. cout << s1.rfind('!') << endl;//12

    5. string类非成员函数

    函数功能说明
    operator+尽量少用,因为传值返回,导致深拷贝效率低
    operator>>输入运算符重载
    operator<<输出运算符重载
    getline获取一行字符串
    relational operators大小比较

    string类的其他接口就不一一列举和介绍了,有需要查文档即可。

    二、string的模拟实现

    1.默认成员函数和成员变量

    为了与原来的string类进行区分,我们在命名空间中模拟实现:

    1. #include
    2. #include
    3. using namespace std;
    4. namespace sss//命名空间
    5. {
    6. class string
    7. {
    8. public:
    9. string(const char* str = "")//构造函数
    10. {}
    11. string(const string& str)//拷贝构造函数
    12. {}
    13. string& operator=(const string& s)//赋值运算符重载函数
    14. {}
    15. ~string()//析构函数
    16. {}
    17. private:
    18. char* _str;
    19. size_t _size;//有效字符个数
    20. size_t _capacity;//存储有效字符串的空间大小(不包含'\0')
    21. static const size_t npos;//表示字符串末尾
    22. };
    23. const size_t string::npos = -1;
    24. }

    1.构造函数

    我们一般会用一个常量字符串来进行初始化,因此用const char* str的指针接收。

    如果str为空字符串,即_size = 0时,多开辟几个字节的空间,防止后续按capacity倍数扩容时,出现capacity=0的情况。

    1. string(const char* str = "")//构造函数
    2. :_size(strlen(str))
    3. {
    4. _capacity = _size == 0 ? 3 : _size;
    5. _str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
    6. strcpy(_str, str);
    7. }

    2.析构函数

    释放开辟的空间,成员变量置空即可。

    1. ~string()//析构函数
    2. {
    3. delete[] _str;
    4. _str = nullptr;
    5. _size = _capacity = 0;
    6. }

    3.拷贝构造函数

    浅拷贝与深拷贝

    我们来调用下方的test1()函数。

    1. using namespace std;
    2. namespace sss
    3. {
    4. class string
    5. {
    6. public:
    7. string(const char* str = "")//构造函数
    8. :_size(strlen(str))
    9. {
    10. _capacity = _size == 0 ? 3 : _size;
    11. _str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
    12. strcpy(_str, str);
    13. }
    14. ~string()//析构函数
    15. {
    16. delete[] _str;
    17. _str = nullptr;
    18. _size = _capacity = 0;
    19. }
    20. private:
    21. char* _str;
    22. size_t _size;//有效字符个数
    23. size_t _capacity;//存储有效字符串的空间大小(不包含'\0')
    24. static const size_t npos;//表示字符串末尾
    25. };
    26. const size_t string::npos = -1;
    27. void test1()
    28. {
    29. string s1("hello world");
    30. string s2(s1);
    31. }
    32. }

    此处发生崩溃,上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。

    调用默认的拷贝构造后,导致的问题是:s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

    浅拷贝:

    编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为 还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

    可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

    深拷贝:

    如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

    先拷贝其大小和容量,再开辟一块新的空间,将其字符串拷贝过去。

    1. string(const string& s)//拷贝构造函数
    2. :_size(s._size)
    3. , _capacity(s._capacity)
    4. {
    5. _str = new char[(_capacity + 1)];
    6. strcpy(_str, s._str);
    7. }

    4.赋值运算符重载函数

    新开辟一块和原字符串相等的空间,将字符串拷贝过去,在释放掉其原本指向的空间。

    1. string& operator=(const string& s)//赋值运算符重载函数
    2. {
    3. if (this != &s)//防止出现自己给自己赋值的情况
    4. {
    5. char* tmp = new char[(s._capacity + 1)];
    6. strcpy(tmp, s._str);
    7. delete[] _str;
    8. _str = tmp;
    9. _size = s._size;
    10. _capacity = s._capacity;
    11. }
    12. return *this;
    13. }

    2.迭代器相关函数的实现

    string类中的迭代器可以把它成是一个char* 的指针。

    1. typedef char* iterator;
    2. typedef const char* const_iterator;
    3. iterator begin();//返回第一个字符的位置
    4. iterator end();//返回最后一个字符的下一个位置
    5. const_iterator begin()const;
    6. const_iterator end()const;
    1. iterator begin()
    2. {
    3. return _str;
    4. }
    5. iterator end()
    6. {
    7. return _str + _size;
    8. }
    9. const_iterator begin()const
    10. {
    11. return _str;
    12. }
    13. const_iterator end()const
    14. {
    15. return _str + _size;
    16. }

    使用演示:

    1. void test2()
    2. {
    3. string s1("hello world");
    4. string::iterator it = s1.begin();
    5. while (it != s1.end())
    6. {
    7. *it += 1;//const迭代器只能读,不能修改
    8. cout << *it;
    9. ++it;
    10. }
    11. //输出结果:ifmmp!xpsme
    12. }

    3.字符串大小与容量相关函数的实现

    1.大小和容量

    1. size_t size()const//返回当前字符串的有效个数
    2. {
    3. return _size;
    4. }
    5. size_t capacity()const//返回当前字符串存储空间的大小
    6. {
    7. return _capacity;
    8. }
    9. bool empty()const//判断字符串是否为空
    10. {
    11. return 0 == _size;
    12. }

    2.reserve 和 resize 函数

    1. void reserve(size_t newCapacity)//预留空间
    2. {
    3. // 如果新容量大于旧容量,则开辟空间
    4. if (newCapacity > _capacity)
    5. {
    6. char* str = new char[newCapacity + 1];
    7. strcpy(str, _str);
    8. // 释放原来旧空间,然后使用新空间
    9. delete[] _str;
    10. _str = str;
    11. _capacity = newCapacity;
    12. }
    13. }
    14. void resize(size_t newSize, char c = '\0')
    15. {
    16. if (newSize > _size)
    17. {
    18. // 如果newSize大于底层空间大小,则需要重新开辟空间
    19. if (newSize > _capacity)
    20. {
    21. reserve(newSize);
    22. }
    23. memset(_str + _size, c, newSize - _size);
    24. }
    25. _size = newSize;
    26. _str[newSize] = '\0';
    27. }

    4.字符串修改操作相关函数的实现

    1.push_back()函数

    1. void push_back(char c)
    2. {
    3. if (_size == _capacity)//判断是否需要扩容
    4. reserve(_capacity * 2);
    5. //调用reserve预留空间
    6. _str[_size++] = c;
    7. _str[_size] = '\0';
    8. }

    2.insert()函数

    1. // 在pos位置上插入字符c/字符串str,并返回该字符的位置
    2. string& insert(size_t pos, char ch)
    3. {
    4. assert(pos <= _size);//确保pos合法
    5. if (_size + 1 > _capacity)
    6. {
    7. reserve(_capacity * 2);
    8. }
    9. size_t end = _size + 1;
    10. while (end > pos)//将pos位置之后的字符向后移动
    11. {
    12. _str[end] = _str[end - 1];
    13. --end;
    14. }
    15. _str[pos] = ch;
    16. ++_size;
    17. return *this;
    18. }
    19. string& insert(size_t pos, const char* str)//方法同上
    20. {
    21. assert(pos <= _size);
    22. size_t len = strlen(str);
    23. if (_size + len > _capacity)
    24. reserve(_size + len);
    25. size_t end = _size + len;
    26. while (end > pos + len - 1)//将pos位置之后的字符向后移动
    27. {
    28. _str[end] = _str[end - len];
    29. --end;
    30. }
    31. strncpy(_str + pos, str, len);
    32. _size += len;
    33. return *this;
    34. }

    3.erase()函数

    第一种情况删除pos位置后的全部字符,直接将pos位置改为'\0',将_size赋值为pos即可。(pos为字符串的下标)

    第二种情况删除pos位置后的部分字符串,将待删除字符串后面的字符串向前移动即可。_size减去删除的长度len。

    1. string& erase(size_t pos, size_t len = npos)
    2. {
    3. //npos表示字符串结束位置
    4. assert(pos <= _size);
    5. if (len == npos || pos + len >= _size)
    6. {
    7. _str[pos] = '\0';
    8. _size = pos;
    9. }
    10. else
    11. {
    12. strcpy(_str + pos, _str + pos + len);
    13. _size -= len;
    14. }
    15. return *this;
    16. }

    4.append函数和 operator+=

    append函数直接复用insert即可,即在字符串末尾追加一个字符串。

    1. void append(const char* str)
    2. {
    3. insert(_size, str);
    4. }

    operator+=直接复用push_back 和 appen即可,即分布追加一个字符和字符串。

    1. string& operator+=(char ch)
    2. {
    3. push_back(ch);
    4. return *this;
    5. }
    6. string& operator+=(const char*str)
    7. {
    8. append(str);
    9. return *this;
    10. }

    5.clear函数

    清空字符串,直接将有效字符个数_size置为0,再将第一个字符置为'\0',将字符串置为空串。

    1. void clear()
    2. {
    3. _size = 0;
    4. _str[_size] = '\0';
    5. }

    6.swap函数

    直接调用标准库里的swap函数,分布交换其成员变量即可。

    1. void swap(string& s)
    2. {
    3. std::swap(_str, s._str);
    4. std::swap(_size, s._size);
    5. std::swap(_capacity, s._capacity);
    6. }

    7.c_str函数

    返回c类型的字符串。

    1. const char* c_str()const
    2. {
    3. return _str;
    4. }

    5.字符串访问相关函数的实现

    1.operator[]

    返回字符串中下标为pos位置的元素。

    1. char& operator[](size_t pos)
    2. {
    3. assert(pos < _size);
    4. return _str[pos];
    5. }
    6. const char& operator[](size_t pos)const
    7. {
    8. assert(pos < _size);
    9. return _str[pos];
    10. }

    2.find()函数

    1. size_t find(char ch, size_t pos = 0)
    2. {
    3. assert(pos <= _size);//确保pos合法
    4. for (size_t i = pos; i < _size; ++i)
    5. {
    6. if (_str[i] == ch)
    7. return i;
    8. }
    9. return npos;
    10. }
    11. size_t find(const char* str, size_t pos = 0)
    12. {
    13. assert(pos <= _size);
    14. char* p = strstr(_str + pos, str);//调用库函数
    15. if (p == nullptr)
    16. return npos;
    17. else
    18. return p - _str;//得到的元素个数,即为目标字符串开头的下标
    19. }

    6.字符串比较相关函数的实现

    字符串比较调用标准库的strcmp函数,其余对已经实现的运算符重载进行复用。

    1. bool operator>(const string& s)const
    2. {
    3. return strcmp(_str, s._str)>0;
    4. }
    5. bool operator==(const string& s)const
    6. {
    7. return strcmp(_str, s._str) == 0;
    8. }
    9. bool operator>=(const string& s)const
    10. {
    11. return (* this>s || *this == s);
    12. }
    13. bool operator<(const string& s)const
    14. {
    15. return !(* this >= s);
    16. }
    17. bool operator<=(const string& s)const
    18. {
    19. return !(*this >s);
    20. }
    21. bool operator!=(const string& s)const
    22. {
    23. return !(*this == s);
    24. }

    7.流插入和流提取的实现

    流插入:逐个字符输出。

    1. ostream& operator<<(ostream& _cout, const sss::string& s)
    2. {
    3. // 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
    4. //cout << s._str;
    5. for (size_t i = 0; i < s.size(); ++i)
    6. {
    7. _cout << s[i];
    8. }
    9. return _cout;
    10. }

    流提取:

    1. istream& operator>>(istream& in, string& s)
    2. {
    3. s.clear();//清空字符串
    4. char ch = in.get();//读取一个字符
    5. char buff[128];//用做缓存数组,避免频繁开辟空间
    6. size_t i = 0;
    7. while (ch != ' ' && ch != '\n')//这两个符号为分割符
    8. {
    9. buff[i++] = ch;
    10. if (i == 127)//当数组满了,将字符串写入目标串
    11. {
    12. buff[127] = '\0';
    13. s += buff;
    14. i = 0;
    15. }
    16. }
    17. if (i != 0)
    18. {
    19. buff[i] = '\0';
    20. s += buff;
    21. }
    22. return in;
    23. }

    8.完整代码

    1. #include
    2. #include
    3. using namespace std;
    4. namespace sss
    5. {
    6. class string
    7. {
    8. public:
    9. string(const char* str = "")//构造函数
    10. :_size(strlen(str))
    11. {
    12. _capacity = _size == 0 ? 3 : _size;
    13. _str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
    14. strcpy(_str, str);
    15. }
    16. string(const string& s)//拷贝构造函数
    17. :_size(s._size)
    18. , _capacity(s._capacity)
    19. {
    20. _str = new char[(_capacity + 1)];
    21. strcpy(_str, s._str);
    22. }
    23. string& operator=(const string& s)//赋值运算符重载函数
    24. {
    25. if (this != &s)
    26. {
    27. char* tmp = new char[(s._capacity + 1)];
    28. strcpy(tmp, s._str);
    29. delete[] _str;
    30. _str = tmp;
    31. _size = s._size;
    32. _capacity = s._capacity;
    33. }
    34. return *this;
    35. }
    36. ~string()//析构函数
    37. {
    38. delete[] _str;
    39. _str = nullptr;
    40. _size = _capacity = 0;
    41. }
    42. typedef char* iterator;
    43. typedef const char* const_iterator;
    44. iterator begin()
    45. {
    46. return _str;
    47. }
    48. iterator end()
    49. {
    50. return _str + _size;
    51. }
    52. const_iterator begin()const
    53. {
    54. return _str;
    55. }
    56. const_iterator end()const
    57. {
    58. return _str + _size;
    59. }
    60. size_t size()const
    61. {
    62. return _size;
    63. }
    64. size_t capacity()const
    65. {
    66. return _capacity;
    67. }
    68. bool empty()const
    69. {
    70. return 0 == _size;
    71. }
    72. void reserve(size_t newCapacity)
    73. {
    74. // 如果新容量大于旧容量,则开辟空间
    75. if (newCapacity > _capacity)
    76. {
    77. char* str = new char[newCapacity + 1];
    78. strcpy(str, _str);
    79. // 释放原来旧空间,然后使用新空间
    80. delete[] _str;
    81. _str = str;
    82. _capacity = newCapacity;
    83. }
    84. }
    85. void resize(size_t newSize, char c = '\0')
    86. {
    87. if (newSize > _size)
    88. {
    89. // 如果newSize大于底层空间大小,则需要重新开辟空间
    90. if (newSize > _capacity)
    91. {
    92. reserve(newSize);
    93. }
    94. memset(_str + _size, c, newSize - _size);
    95. }
    96. _size = newSize;
    97. _str[newSize] = '\0';
    98. }
    99. void push_back(char c)
    100. {
    101. if (_size == _capacity)
    102. reserve(_capacity * 2);
    103. _str[_size++] = c;
    104. _str[_size] = '\0';
    105. }
    106. // 在pos位置上插入字符c/字符串str,并返回该字符的位置
    107. string& insert(size_t pos, char ch)
    108. {
    109. assert(pos <= _size);
    110. if (_size + 1 > _capacity)
    111. {
    112. reserve(_capacity * 2);
    113. }
    114. size_t end = _size + 1;
    115. while (end > pos)
    116. {
    117. _str[end] = _str[end - 1];
    118. --end;
    119. }
    120. _str[pos] = ch;
    121. ++_size;
    122. return *this;
    123. }
    124. string& insert(size_t pos, const char* str)
    125. {
    126. assert(pos <= _size);
    127. size_t len = strlen(str);
    128. if (_size + len > _capacity)
    129. reserve(_size + len);
    130. size_t end = _size + len;
    131. while (end > pos + len - 1)
    132. {
    133. _str[end] = _str[end - len];
    134. --end;
    135. }
    136. strncpy(_str + pos, str, len);
    137. _size += len;
    138. return *this;
    139. }
    140. string& erase(size_t pos, size_t len = npos)
    141. {
    142. assert(pos <= _size);
    143. if (len == npos || pos + len >= _size)
    144. {
    145. _str[pos] = '\0';
    146. _size = pos;
    147. }
    148. else
    149. {
    150. strcpy(_str + pos, _str + pos + len);
    151. _size -= len;
    152. }
    153. return *this;
    154. }
    155. void append(const char* str)
    156. {
    157. insert(_size, str);
    158. }
    159. string& operator+=(char ch)
    160. {
    161. push_back(ch);
    162. return *this;
    163. }
    164. string& operator+=(const char* str)
    165. {
    166. append(str);
    167. return *this;
    168. }
    169. void clear()
    170. {
    171. _size = 0;
    172. _str[_size] = '\0';
    173. }
    174. void swap(string& s)
    175. {
    176. std::swap(_str, s._str);
    177. std::swap(_size, s._size);
    178. std::swap(_capacity, s._capacity);
    179. }
    180. const char* c_str()const
    181. {
    182. return _str;
    183. }
    184. char& operator[](size_t index)
    185. {
    186. assert(index < _size);
    187. return _str[index];
    188. }
    189. const char& operator[](size_t index)const
    190. {
    191. assert(index < _size);
    192. return _str[index];
    193. }
    194. size_t find(char ch, size_t pos = 0)
    195. {
    196. assert(pos <= _size);
    197. for (size_t i = pos; i < _size; ++i)
    198. {
    199. if (_str[i] == ch)
    200. return i;
    201. }
    202. return npos;
    203. }
    204. size_t find(const char* str, size_t pos = 0)
    205. {
    206. assert(pos <= _size);
    207. char* p = strstr(_str + pos, str);
    208. if (p == nullptr)
    209. return npos;
    210. else
    211. return p - _str;
    212. }
    213. bool operator>(const string& s)const
    214. {
    215. return strcmp(_str, s._str) > 0;
    216. }
    217. bool operator==(const string& s)const
    218. {
    219. return strcmp(_str, s._str) == 0;
    220. }
    221. bool operator>=(const string& s)const
    222. {
    223. return (*this > s || *this == s);
    224. }
    225. bool operator<(const string& s)const
    226. {
    227. return !(*this >= s);
    228. }
    229. bool operator<=(const string& s)const
    230. {
    231. return !(*this > s);
    232. }
    233. bool operator!=(const string& s)const
    234. {
    235. return !(*this == s);
    236. }
    237. private:
    238. friend ostream& operator<<(ostream& _cout, const sss::string& s);
    239. friend istream& operator>>(istream& _cin, sss::string& s);
    240. private:
    241. char* _str;
    242. size_t _size;//有效字符个数
    243. size_t _capacity;//存储有效字符串的空间大小(不包含'\0')
    244. static const size_t npos;//表示字符串末尾
    245. };
    246. const size_t string::npos = -1;
    247. ostream& operator<<(ostream& _cout, const sss::string& s)
    248. {
    249. // 不能使用这个, 因为string的字符串内部可能会包含\0
    250. // 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
    251. //cout << s._str;
    252. for (size_t i = 0; i < s.size(); ++i)
    253. {
    254. _cout << s[i];
    255. }
    256. return _cout;
    257. }
    258. istream& operator>>(istream& in, string& s)
    259. {
    260. s.clear();//清空字符串
    261. char ch = in.get();//读取一个字符
    262. char buff[128];//用做缓存数组,避免频繁开辟空间
    263. size_t i = 0;
    264. while (ch != ' ' && ch != '\n')//这两个符号为分割符
    265. {
    266. buff[i++] = ch;
    267. if (i == 127)//当数组满了,将字符串写入目标串
    268. {
    269. buff[127] = '\0';
    270. s += buff;
    271. i = 0;
    272. }
    273. }
    274. if (i != 0)
    275. {
    276. buff[i] = '\0';
    277. s += buff;
    278. }
    279. return in;
    280. }
    281. }
  • 相关阅读:
    php session 的封装 (收藏)
    最全前端面试总结(2)map/reduce
    【无标题】
    Meta Learning
    【FreeRTOS】04 FreeRTOS 创建任务相关API函数
    538. 把二叉搜索树转换为累加树
    Ubuntu上搭建FTP服务
    线程池原理与实现
    C++ 异形窗口
    在VMD上可视化hdf5格式的分子轨迹文件
  • 原文地址:https://blog.csdn.net/Bottle2023/article/details/132942083