当前位置: 首页 > news >正文

008-C++String

string类

1. string类存在的原因

1.1 C语言中的字符串

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

1.2 C++的string类

C++STL库中将字符串封装成了string类,并提供了相应的结构,这样我们对字符串的使用就能更加方便快捷,并且不用担心越界等风险。

2. 标准库中的string类

2.1 string类

C++文档中的介绍:string - C++ Reference — string - C++ Reference

Strings are objects that represent sequences of characters.

The standard string class provides support for such objects with an interface similar to that of a standard container of bytes, but adding features specifically designed to operate with strings of single-byte characters.

The string class is an instantiation of the basic_string class template that uses char (i.e., bytes) as its character type, with its default char_traits and allocator types (see basic_string for more info on the template).

Note that this class handles bytes independently of the encoding used: If used to handle sequences of multi-byte or variable-length characters (such as UTF-8), all members of this class (such as length or size), as well as its iterators, will still operate in terms of bytes (not actual encoded characters).

总结:

  1. string是表示字符串的字符串类

  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;

  4. 不能操作多字节或者变长字符的序列。

在使用string类时必须包含头文件#include <string>,string类也在std命名空间中。

2.2 string类的常用结构

  1. string类常见构造

    函数名称功能说明
    string()构造空的string类对象,即空字符串
    string(const char* s)用C的字符串来构造string类对象
    string(size_t n, char c)string类对象中包含n个字符c
    string(const string& s)拷贝构造函数
    int main()
    {string s1; // 空字符串string s2("hello string"); // 使用C语言字符串进行构造string s3(9, 'a'); // 创建一个包含9个'a'字符的字符串string s4(s2); //拷贝构造return 0;
    }
    
  2. string类对象的容量操作

    函数名称功能说明
    size返回字符串有效字符长度
    length返回字符串有效字符长度
    capacity返回空间总大小
    empty检测字符串释放为空串,是返回true,否则返回false
    clear清空有效字符
    reserve为字符串预留空间
    resize将有效字符的个数改成n个,多出的空间用字符c填充,如果未指定字符,则填充0
    int main()
    {string s1("hello string");cout << s1.size() << endl;cout << s1.length() << endl;cout << s1.capacity() << endl;cout << s1.empty() << endl;s1.clear();cout << s1.empty() << endl;s1.reserve(20);cout << s1.capacity() << endl;s1.resize(30, '0');cout << s1 << endl;return 0;
    }
    

    在这里插入图片描述

    注意:

    • size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
    • clear()只是将string中有效字符清空,不改变底层空间大小。
    • resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
    • reserve(size_t n=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
  3. string类对象的访问及遍历操作

    函数名称功能说明
    operator[]返回字符串对应下标位置的引用(与C语言字符串功能类似)
    begin、endbegin()获取一个字符的迭代器、end()获取最后一个字符下一个位置的迭代器
    rbegin、rendrbegin()获取一个字符的反向迭代器、rend()获取最后一个字符下一个位置的反向迭代器
    cbegin、cend与begin、end不同的是,这个是const修饰的迭代器,也就是不允许修改指向的内容
    crbegin、crendconst修饰的反向迭代器。

    关于迭代器:

    • 暂时可以将它理解成一个指针,可以进行解引用操作。
    • begin就是第一个字符串中第一个位置的指针,end就是指向字符串中最后一个位置下一个位置的指针(左闭右开),rbegin和rend正好相反,rbegin是指向最后一个位置的指针,rend是指向第一个位置的前一个位置的指针。
    • 对正向迭代器进行++操作会将迭代器往后移一个位置,对反向迭代器++则是将迭代器往前移一个位置。
    • string中正向迭代器的类型是string::iterator、反向迭代器则是string::reverse_iterator
    int main()
    {string s1("hello string");cout << s1[0] << endl;auto it = s1.begin(); // 因为迭代器类型较长,这里可以使用auto自动识别,更加方便一些while (it != s1.end())cout << *it++;cout << endl;auto rit = s1.rbegin();while (rit != s1.rend())cout << *rit++;cout << endl;return 0;
    }
    

    在这里插入图片描述

    补充:string类的对象也同样支持前面说过的范围for(详见:001-C++入门-CSDN博客文章中9. 基于范围的for循环)

  4. string类对象的修改操作

    函数名称功能说明
    push_back在字符串后尾插字符
    append在字符串后追加一个字符串
    operator+=在字符串后追加一个字符串
    c_str返回C格式字符串(const char*)
    find从字符串指定位置开始往后找字符c或字符串,不传指定位置默认从第一个位置开始找,返回该字符在字符串中的位置,没找到返回npos(size_t的最大值,转成int本质上就是-1)。
    rfind从字符串指定位置开始往前找字符c,不传指定位置默认从最后一个位置开始找,返回该字符在字符串中的位置,没找到返回npos
    substr在str中从pos位置开始,截取n个字符,然后将其返回。
    int main()
    {string s;s.push_back('a');s.append("bb");s += "ccc";printf("%s\n", s.c_str());cout << s.find('a') << ' ' << (int)s.find('d') << endl;cout << s.rfind('c') << ' ' << (int)s.rfind('d') << endl;cout << s.substr(0, 3) << endl;return 0;
    }
    

    在这里插入图片描述

    说明:

    • 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
    • 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好,这样可以减少底层扩容的次数,提升效率。
  5. string类非成员函数

    函数名称功能说明
    operator+将两个字符串拼接称一个字符串返回,尽量少用,因为传值返回,导致深拷贝效率低
    operator>>输入运算符重载(读取到空格就停止)
    operator<<输出运算符重载
    getline从指定流中获取一行字符串(读取到换行符才停止)
    relaticonal operators(关系运算符重载,也就是<、<=、>、>=、==、!=的重载)大小比较
    int main()
    {string s1("aaa");string s2("bbb");cout << s1 + s2 << endl;string s3;cout << "输入:";getline(cin, s3);cout << s3 << endl;return 0;
    }
    

    在这里插入图片描述

  6. vs和g++下string结构的说明

    注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。

    • vs下string的结构

      string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

      • 当字符串长度小于16时,使用内部固定的字符数组来存放
      • 当字符串长度大于等于16时,从堆上开辟空间
      union _Bxty
      { 	// storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
      } _Bx;
      

      这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

      其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量。

      最后:还有一个指针做一些其他事情。

      故总共占16+4+4+4=28个字节。

      在这里插入图片描述

    • g++下string的结构

      g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

      • 空间总大小
      • 字符串有效长度
      • 引用计数
      struct _Rep_base
      {size_type 		_M_length;size_type 		_M_capacity;_Atomic_word 	_M_refcount;
      }
      
      • 指向堆空间的指针,用来存储字符串。

3.string类的模拟实现

这里我们只实现部分上面提及的常用接口。

String.hpp

#pragma once#include <iostream>namespace my
{class String{friend void swap(String& s1, String& s2);public:typedef char* iterator;typedef const char* const_iterator;String(const char* s = ""):_size(strlen(s)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, s);_str[_size] = '\0';}String(size_t n, char c):_capacity(n),_size(n){_str = new char[_capacity + 1];for (int i = 0; i < _size; i++)_str[i] = c;_str[_size] = '\0';}String(const String& s){String tmp(s._str);swap(*this, tmp);}String& operator=(String s){swap(*this, s);return *this;}~String(){delete[] _str;}size_t size(){return _size;}size_t length(){return _size;}size_t capacity(){return _capacity;}bool empty(){return _size == 0;}void clear(){_str[0] = '\0';_size = 0;}void reserve(size_t n){if (n > _capacity || (n < _capacity && n > _size)){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char c = '\0'){if (n < _size){_str[n] = '\0';_size = n;return;}if (n > _capacity)reserve(n);while (_size < n)_str[_size++] = c;_str[_size] = '\0';}char& operator[](size_t n){return _str[n];}const char& operator[](size_t n) const{return _str[n];}const char* c_str() const{return _str;}iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator cbegin() const{return _str;}const_iterator rend() const{return _str + _size;}void push_back(char c){if (_capacity == _size){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;char* tmp = new char[newCapacity + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;}_str[_size++] = c;_str[_size] = '\0';}String& operator+=(const char* s){size_t sz = strlen(s);reserve(sz + _size);for (int i = 0; i < sz; i++)push_back(s[i]);return *this;}String& operator+=(char c){push_back(c);return *this;}String& operator+=(const String& s){reserve(s._size + _size);for (int i = 0; i < s._size; i++)push_back(s[i]);return *this;}size_t find(char c, size_t pos = 0){for (int i = pos; i < _size; i++)if (_str[i] == c)return i;return npos;}size_t find(const char* s, size_t pos = 0){char* p = strstr(_str + pos, s);if (p != nullptr) return p - _str;return npos;}String substr(size_t pos, size_t len = npos){String sub;len = std::min(len, _size - pos);for (int i = pos; i < pos + len; i++)sub += _str[i];return sub;}private:char* _str;size_t _capacity;size_t _size;public:static const size_t npos;};const size_t String::npos = -1;String operator+(String s1, String& s2){s1 += s2;return s1;}std::istream& operator>>(std::istream& in, String& s){s.clear();char c;char tmp[128];int i = 0;c = in.get();while (c != '\n' && c != ' '){tmp[i++] = c;if (i == 127){tmp[i] = '\0';s += tmp;i = 0;}c = in.get();}if (i != 0){tmp[i] = '\0';s += tmp;}return in;}std::ostream& operator<<(std::ostream& out, String& s){out << s.c_str();return out;}bool operator<(String& s1, String& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator==(String& s1, String& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator<=(String& s1, String& s2){return s1 < s2 || s1 == s2;}bool operator>(String& s1, String& s2){return !(s1 <= s2);}bool operator>=(String& s1, String& s2){return !(s1 < s2);}bool operator!=(String& s1, String& s2){return !(s1 == s2);}std::istream& getline(std::istream& in, String& s){s.clear();char c;char tmp[128];int i = 0;c = in.get();while (c != '\n'){tmp[i++] = c;if (i == 127){tmp[i] = '\0';s += tmp;i = 0;}c = in.get();}if (i != 0){tmp[i] = '\0';s += tmp;}return in;}void swap(String& s1, String& s2){std::swap(s1._str, s2._str);std::swap(s1._capacity, s2._capacity);std::swap(s1._size, s2._size);}
}

相关文章:

  • VS如何编译QuaZip库
  • 【会议征稿中!!!】2025年现代管理、物流与供应链国际会议(MMLSC 2025)
  • 中国移动6周年!
  • 行为型设计模式之Chain of Responsibility(责任链)
  • 【笔记】旧版MSYS2 环境中 Rust 升级问题及解决过程
  • 什么是权威解析服务器?权威解析服务器哪些作用?
  • Xshell 详细安装与配置教程:从下载到高效使用
  • PostgreSQL 技术峰会,聚焦国产生态与前沿技术
  • Java调用大模型API实战指南
  • QT: `long long` 类型转换为 `QString` 2025.6.5
  • 栈的概念以及实现
  • 超大规模芯片验证:基于AMD VP1902的S8-100原型验证系统实测性能翻倍
  • 智能化弱电工程项目管理培训
  • Kyosan K5BMC ELECTRONIC INTERLOCKING MANUAL 电子联锁
  • Java并发编程实战 Day 12:阻塞队列与线程协作
  • 【论文+硬件】HOMIE:定制外骨骼 、手套和脚踏座舱低成本操控人形机器人+强化学习自主下蹲抓取物体 框架
  • Day 41 训练
  • C语言字符数组初始化的5种方法(附带实例)
  • 使用 C/C++ 和 OpenCV 实现滑动条控制图像旋转
  • 04 Deep learning神经网络编程基础 梯度下降 --吴恩达
  • 政府网站建设招标方案/武汉新闻最新消息
  • 企业网络营销青岛/优化神马排名软件
  • wordpress主题几个网站/品牌营销策略分析论文
  • 周口网站制作公司哪家好/怎么被百度收录
  • php网站做分享到朋友圈/爱站网络挖掘词
  • 菏泽 做网站 多少钱/下载爱城市网app官方网站