【C++】String的语法及常用接口底层模拟实现

article/2025/8/17 13:47:21

【C++】String的语法及常用接口底层模拟实现

  • 一、string类(了解)
  • 二、string类的常用接口
    • 2.1 string类对象的常见构造
    • 2.2 string类对象的容量操作
    • 2.3 string类对象的访问及遍历操作
    • 2.4 string类对象的修改操作
    • 2.5 string类非成员函数
  • 三、string类常用接口的模拟实现
    • 3.1 初建构造
    • 3.2 赋值重载
    • 3.3 返回容量大小与【】
    • 3.4 reserve,resize
    • 3.5 插入(push_back,append,operator+=,insert)
    • 3.6 删除(erase),清空(clear)
    • 3.7 查找(find)
    • 3.8 返回子串(substr)
    • 3.9 比较符号(<,>,==,<=,>=,!=)
    • 3.10 迭代器(iterator)
    • 3.11 流插入(<<),流提取(>>)
  • 四、总结

一、string类(了解)

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
  4. 不能操作多字节或者变长字符的序列。在使用string类时,必须包含#include头文件以及using namespace std;
  • C++ 的 std::string 是一个动态字符数组,支持多种操作,且是可变的。在使用string类时,必须包含#include头文件以及using namespace std;

二、string类的常用接口

2.1 string类对象的常见构造

  • 函数名称 -------功能说明
  • string() -------- 构造空的string类对象,即空字符串
  • string(const char * s) -------- 用C-string来构造string类对象
  • string(size_t n, char c) ------- string类对象中包含n个字符c
  • string(const string&s) -------- 拷贝构造函数
void Teststring()
{string s1; // 构造空的string类对象s1string s2("hello bit"); // 用C格式字符串构造string类对象s2string s3(s2); // 拷贝构造s3
}

2.2 string类对象的容量操作

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

在这里插入图片描述

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

2. clear()只是将string中有效字符清空,不改变底层空间大小。
3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。
[注意]:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

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

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

在这里插入图片描述

2.4 string类对象的修改操作

  • 函数名称 ------- 功能说明
  • push_back ------- 在字符串后尾插字符c
  • append ------- 在字符串后追加一个字符串
  • operator+= ------- 在字符串后追加字符串str
  • c_str ------- 返回C格式字符串
  • find + npos ------- 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
  • rfind ------- 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
  • substr ------- 在str中从pos位置开始,截取n个字符,然后将其返回
    在这里插入图片描述

注意:
1. 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

2.5 string类非成员函数

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

三、string类常用接口的模拟实现

3.1 初建构造

  • 我们运用一个字符数组来存string,私有部分包括字符大小size,和空间容量capacity,_str字符数组
  • 构造函数的形参运用缺省参数,当不传参时,将会变为空串,运用strlen得到传入的字符串大小,再new一个容量较大的字符数组,让_str指向这个数组,最后用memcpy,将传入的字符串内容拷贝给_str
  • 拷贝构造思路与构造函数思路相同
  • 析构函数将_str释放,再将size与capacity置为0
  • 再写一个c_str接口,方便打印查看,与c语言适配
class string
{
public:const static size_t npos = -1;//设置一个npos后面要用,类型为size_t所以这里的npos是一个非常大的整数//构造函数string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];memcpy(_str, str, _size + 1);}//拷贝构造string(const string& s){_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}//c_strconst char* c_str()const{return _str;}
private:size_t _size;size_t _capacity;char* _str;
};

3.2 赋值重载

先写一个自定义的交换string的函数,将传入的形参与this的数据交换,最后返回*this

//赋值重载
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string& operator=(string tmp)
{swap(tmp);return *this;
}

3.3 返回容量大小与【】

//size,capacity
size_t size()const
{return _size;
}
size_t capacity()const
{return _capacity;
}
//[]模拟实现
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}

3.4 reserve,resize

  • reserve:如果需要的空间n大于原空间,则要进行扩容,创建一个临时变量tmp指向new的新空间,运用memcpy将原数据拷贝给tmp,将原来_str所在空间释放,再将_str只向tmp所指向的空间,size不变,capacity变为扩容后的n
  • resize:如果是缩小,n小于原来的size,直接在下标为n的位置置为\0。如果是扩大,先用reserve扩容,再将ch放入字符数组
//reserve模拟实现
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];memcpy(tmp, _str,_size + 1);delete[]_str;_str = tmp;_capacity = n;}
}
//resize模拟实现
void resize(size_t n, char ch = '\0')
{if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}

3.5 插入(push_back,append,operator+=,insert)

  • 插入的主要思想,就是空间不够就扩容,然后将字符放入数组,写出push_back和append,就可以复用写出+=,insert
//push_back模拟实现
void push_back(char ch)
{if (_size == _capacity){//2倍扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';
}//append模拟实现
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}memcpy(_str + _size, str, len + 1);_size += len;
}//+=模拟实现
string& operator+=(char ch)
{push_back(ch);return *this;
}
string& operator+=(const char* str)
{append(str);return *this;
}//insert模拟实现
void insert(size_t pos, size_t n, char ch)
{assert(pos <= _size);if(_size + n > _capacity){reserve(_size + n);}size_t end = _size;while (end >= pos && end != npos){//当end==-1时,因为类型为size_t 所以会变为整数的最大值//也就是npos,所以多加一个条件控制whlie_str[end + n] = _str[end];--end;}for (int i = 0; i < n; i++){_str[i + pos] = ch;}_size += n;
}
void insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size);}size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;
}

3.6 删除(erase),清空(clear)

  • erase:实现的从某个位置开始删除,删除长度为 len 的字符。len有一个缺省参数,为npos(npos是一个很大的数,也就是不传参给 len 的话,默认删除到最后)。如果 len 本就很大,删除的长度超过从pos开始所剩余的长度,那么默认也是pos后的删除完。
  • claer:与erase不同,它是将整个字符串全部清空
//erase模拟实现
void erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (len == npos || len + pos >= _size){_str[pos] = '\0';_size = pos;}else{size_t end = len + pos;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}
}
//clear模拟实现
void clear()
{_str[0] = '\0';_size = 0;
}

3.7 查找(find)

从pos位置开始查找,查找的内容可能是一个字符,也可能是一个子串。如果找到,则返回其下标。没找到就返回npos。

//find模拟实现
size_t find(char ch, size_t pos = 0)
{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}
size_t find(const char* str, size_t pos = 0)
{assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}else{return npos;}}

3.8 返回子串(substr)

从某个位置开始查找,查找长度为 len 的字符。len有一个缺省参数,为npos(npos是一个很大的数,也就是不传参给 len 的话,默认返回到最后)。如果 len 本就很大,返回子串的长度超过从pos开始所剩余的长度,那么默认也是pos后的子串全部返回。

//substr模拟实现
string substr(size_t pos = 0, size_t len = npos)
{assert(pos < _size);size_t n = len;if (len == npos || len + pos >=_size){n = _size - pos;}string tmp;tmp.reserve(n);for (size_t i = pos; i < pos + n; i++){tmp += _str[i];}return tmp;
}

3.9 比较符号(<,>,==,<=,>=,!=)

字符串的比较并非比较其长度,而是与其相同位置字符的大小有关,也就是我们所说的字典序。我们这里只需要实现其中的两个,其他均可复用。

bool operator<(const string& s)const
{//先用memcmp比较两个字符串较短的部分(前半段长度相等的部分)//如果相等,ret为0,//则size小的字符串小int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);// "hello" "hello"   false// "helloxx" "hello" false// "hello" "helloxx" truereturn ret == 0 ? _size < s._size : ret < 0;
}
bool operator==(const string& s)const
{return _size == s._size&& memcmp(_str, s._str, _size) == 0;
}
bool operator<=(const string& s)const
{return *this < s || *this == s;
}
bool operator>(const string& s)const
{return !(*this <= s);
}
bool operator>=(const string& s)const
{return !(*this < s);
}
bool operator!=(const string& s)const
{return !(*this == s);
}

3.10 迭代器(iterator)

这里的begin()就是返回的字符串的首元素地址,end()返回的是字符串最后一个元素的后一个地址!

public:typedef char* iterator;typedef const char* const_iterator;
//迭代器模拟实现
iterator begin()
{return _str;
}iterator end()
{return _str + _size;
}const_iterator begin() const
{return _str;
}const_iterator end() const
{return _str + _size;
}

3.11 流插入(<<),流提取(>>)

//流插入,流提取
ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}
istream& operator>>(istream& in, string& s)
{//这里我们采用一个一个字符的输入//覆盖之前输入数据s.clear();char ch = in.get();//处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}//每次输入的字符大小不确定,如果提前用reserve开空间可能导致浪费//因此先用一个大小为128字符数组存输入的字符,当数组满时再放入stringchar buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}

四、总结

string是C++中比较重要的且是入门时必须所学的容器。在平常中使用的频率较高,所以我们不仅要掌握其简单的用法,更应该去了解其底层的实现。这有助于我们后续的使用和理解。string常用的语法和接口的底层实现,这些都是我们应该熟练掌握的内容。

关于string类的讲解就到这里,希望对你有所帮助,感谢观看ovo!


http://www.hkcw.cn/article/ASwuJiiYhM.shtml

相关文章

【C++庖丁解牛】C++11---统一的列表初始化 | auto | decltype | nullptr | STL中一些变化

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. C11简介2. 统一的列表…

STL:位图和布隆过滤器

一&#xff0c;位图 1.1 位图的概念 究竟什么是位图呢&#xff1f;&#xff1f;我们用一道问题来引入 问题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在 这40亿个数中。【腾讯】 根据这个问题&#x…

【C++高阶】:智能指针的全面解析

✨ 落絮无声春堕泪&#xff0c;行云有影月含羞 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&a…

【C++对于C语言的扩充】函数重载、引用以及内联函数

文章目录 &#x1f680;前言&#x1f680;函数重载注意&#xff1a;✈️为什么C可以实现函数重载&#xff0c;而C语言却不行呢&#xff1f; &#x1f680;引用✈️引用的特性✈️C中为什么要引入引用✈️引用与指针的区别 &#x1f680;内联函数✈️内联函数特性 &#x1f680;…

【C++指南】STL容器的安全革命:如何封装Vector杜绝越界访问与迭代器失效?

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 使用STL的三个境界&#xff1a;能用&#xff0c;明理&#xff0c;能扩展 &#x1f44d; 如果觉得这篇文章有帮助&#…

C++ 异常处理机制与自定义异常体系

目录 1.C语言传统的处理错误的方式 &#x1f60a; 1. 终止程序 2. 返回错误码 3.实际使用中的情况 2. C异常概念&#x1f33c; 2.1 C异常的基本概念 2.2异常的抛出和匹配原则 2.3 异常的重新抛出 2.4 异常安全 2.5 异常规范 3. 自定义异常体系 &#x1f495;&#x…

C++入门看这一篇就够了——超详细讲解(120000多字详细讲解,涵盖C++大量知识)

目录 一、面向对象的思想 二、类的使用 1.类的构成 2.类的设计 三、对象的基本使用 四、类的构造函数 1.构造函数的作用 2.构造函数的特点 3.默认构造函数 3.1.合成的默认构造函数 3.2.手动定义的默认构造函数 四、自定义的重载构造函数 五、拷贝构造函数 1.手动…

【第53节】Windows编程必学之使用C++写exe压缩加密壳

目录 一、实现背景 1.1 前言 1.2 前置知识 1.3 达到目标 二、壳的实现要点 2.1 写壳怎么做 2.2 写壳的困难点 2.3 如何写壳代码 2.4 API函数的调用问题 2.5 重定位问题 2.6 信息交互问题 2.7 调试问题 2.8 关于目标程序的随机基址 2.9 关于目标程序的导入表 2.1…

C++离线查询

前言 C算法与数据结构 打开打包代码的方法兼述单元测试 概念及原理 离线算法( offline algorithms)&#xff0c;离线计算就是在计算开始前已知所有输入数据&#xff0c;输入数据不会产生变化&#xff0c;且在解决一个问题后就要立即得出结果的前提下进行的计算。 通俗的说&a…

金价又涨了!金饰克价涨至1018元,一夜涨14元

美东时间5月23日,国际贵金属期货普遍收涨,COMEX黄金期货涨1.90%,报3357.70美元/盎司,本周累计上涨4.75%。5月24日,国内金饰价格跟涨。周生生足金饰品标价1018元/克,较前一日1004元/克的价格上涨14元/克。责任编辑:zx0002

日本人准备开始吃饲料了?

日本农业水产大臣小泉进次郎十分骄傲地宣布政府将要拿出2021年所产陈米以每5公斤1800日元的价格进行售卖(合人民币差不多1斤大米9块钱)。当地专家吹捧此举将有效缓解日本米荒,并放话越是陈米吃着越香,这下日本人有口福了结果评论区直接翻车了,有网友直接贴出往年饲料米价格…

国际乒联发声明回应选举争议 谴责扰乱行为并重启会议

当地时间29日,国际乒联发布了关于2025年度代表大会期间选举事宜的声明。5月27日,在卡塔尔多哈举行的国际乒联年度股东大会上,因主席选举争议引发混乱,会议最终宣布临时暂停。声明中提到,主席选举结束后,一些既不是会员协会代表也不是执行委员会、理事会、委员会成员或受邀…

胖东来红内裤案宣判:“段某”赔偿40万元 名誉权获法院支持

2025年5月28日,许昌市魏都区人民法院公开审理了许昌市胖东来商贸集团有限公司与段某之间的名誉权纠纷案。法院判决段某在其个人抖音账号“两个小段(小)”发布书面道歉信的视频,并赔偿胖东来公司40万元经济损失。部分人大代表、政协委员、媒体记者、律师代表和企业代表旁听了…

市监总局就毕井泉被查表态 再度引发市场关注

六年多前,毕井泉因长春长生疫苗案从原国家食品药品监督管理总局局长位置引咎辞职的消息震惊了市场;六年多后,他被查的消息再次引发市场的强烈关注。据中央纪委国家监委网站5月29日消息,十四届全国政协常委、经济委员会副主任毕井泉涉嫌严重违纪违法,目前正接受中央纪委国家…

高芙评职业生涯最经典三胜 荣耀时刻回顾

近日,美国网球运动员高芙在法网接受记者采访时,回顾了自己职业生涯中的三场经典胜利。这三场比赛分别是2024年终总决赛争冠战对阵郑钦文、2019年温网第一轮对阵大威廉姆斯以及2023年美网决赛对阵萨巴伦卡。她还特别提到了此前罗马半决赛与郑钦文的那场长达三个半小时的大战,…

女子露营归来脖子惨遭“毁容” 提醒:夏季蚊虫活跃,如遇皮肤瘙痒红肿不能拖

近日,浙江30岁女子小妍露营归来后,颈部便出现刺痛和瘙痒,起初她并未在意。两天后,症状急剧加重——皮肤红肿成片,冒出红色丘疹和水疱,还伴随灼热疼痛。无独有偶,小学生骏骏在户外骑车后,小腿处的皮肤上也出现了个大包。两人来浙江省皮肤病医院就医后,均被确诊为“虫咬…

男子乘火车旅行刷新吉尼斯纪录:24小时内乘火车旅行5887.76公里

近日,吉尼斯世界纪录官网公布了一项纪录——中国男子王冬成功以24小时内5887.76公里的火车旅行距离,刷新了“24小时内乘坐火车旅行最远距离”的吉尼斯世界纪录。▲王冬刷新吉尼斯世界纪录今年39岁的王冬是四川德阳人,12年前在上海求学时的他,就曾因换乘8趟列车回家而走红网…

从外卖APP到网络协议:深入解析UDP及应用层协议

目录 1. 应用层和传输层1.1 开发中常见的自定义协议格式 2. UDP2.1 源端口号及目的端口号2.2 UDP报文长度2.3 UDP校验和(checksum) 3. 基于UDP的应用层协议 关注我&#xff0c;学习更多企业开发和面试内容~ 1. 应用层和传输层 应用层和程序员接触最密切&#xff0c;应用程序&a…

【JavaWeb】基本概念、web服务器、Tomcat、HTTP协议

目录 1. 基本概念1.1 基本概念1.2 web应用程序1.3 静态web1.4 动态web 2. web服务器3. tomcat详解3.1 安装3.2 启动3.3 配置3.3.1 配置启动的端口号3.3.2 配置主机的名称3.3.3 其他常用配置项日志配置数据源配置安全配置 3.4 发布一个网站 4. Http协议4.1 什么是http4.2 http的…

自扶正救生艇,乘风破浪,守护生命

在复杂水域救援中存在显著缺陷。遇巨浪或急流漩涡易倾覆且无法自主复位&#xff0c;使救援人员与被困者陷入二次危险。统计显示&#xff0c;激流救援中近三成五的救援人员伤亡源于船只倾覆后被困。更严重的是&#xff0c;传统救生艇倾覆后常需外部救援力量才能恢复&#xff0c;…