news 2026/6/21 0:14:53

11.C++入门:String|auto|范围for|常用接口

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
11.C++入门:String|auto|范围for|常用接口

STL简介

什么是STL

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架

STL的六大组件
  1. 仿函数:greater、less、…
  2. 算法:find、swap、reverse、sort、merge、…
  3. 迭代器:iterator、const_iterator、reverse_iterator、const_reverse_iterator
  4. 空间配置器:allocator
  5. 容器:string、vector、list、deque、map、set、multimap、multiset
  6. 配接器:stack、queue、priority_queue

C语言中的字符串

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

标准库中的string类

string类

cplusplus.com/reference/string/string/?kw=string
在使用string类时,必须包含#include头文件以及using namespace std;

auto和范围for

auto关键字

  • 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
  • 用auto声明指针类型时,用autoauto*没有任何区别,但用auto声明引用类型时则必须加&
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
  • auto不能直接用来声明数组
#include<iostream> using namespace std; int func1() { return 10; } // 不能做参数 void func2(auto a) {} // 可以做返回值,但是建议谨慎使用 auto func3() { return 3; } int main() { int a = 10; auto b = a; auto c = 'a'; auto d = func1(); // 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项 auto e; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; int x = 10; auto y = &x; auto* z = &x; auto& m = x; cout << typeid(x).name() << endl; cout << typeid(y).name() << endl; cout << typeid(z).name() << endl; auto aa = 1, bb = 2; // 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型 auto cc = 3, dd = 4.0; // 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型 auto array[] = { 4, 5, 6 }; return 0; }
#include<iostream> #include <string> #include <map> using namespace std; int main() { std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} }; // auto的用武之地 //std::map<std::string, std::string>::iterator it = dict.begin(); auto it = dict.begin(); while (it != dict.end()) { cout << it->first << ":" << it->second << endl; ++it; } return 0; }

范围for

  • 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
  • 范围for可以作用到数组和容器对象上进行遍历
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
#include<iostream> #include <string> #include <map> using namespace std; int main() { int array[] = { 1, 2, 3, 4, 5 }; // C++98的遍历 for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i) { array[i] *= 2; } for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i) { cout << array[i] << endl; } // C++11的遍历 for (auto& e : array) e *= 2; for (auto e : array) cout << e << " " << endl; string str("hello world"); for (auto ch : str) { cout << ch << " "; } cout << endl; return 0; }
string类的常用接口说明
  1. string类对象的常见构造
(constructor)函数名称功能说明
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类对象s1 string s2("hello bit"); // 用C格式字符串构造string类对象s2 string s3(s2); // 拷贝构造s3 }
  1. string类对象的容量操作
函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串是否为空串,是返回 true,否则返回 false
clear清空有效字符
reserve为字符串预留空间 **
resize将有效字符的个数改成 n 个,多出的空间用字符 c 填充
扩容
// reserve 保留 // reverse 反转 逆置 int main() { try { string s1; string s2("hello world"); // 实践中没有参考和使用的价值 cout << s1.max_size() << endl; cout << s2.max_size() << endl; s1.reserve(s1.max_size()); } catch (const exception& e) { cout << e.what() << endl; } return 0; }

检查扩容情况

int main() { try { string s1; string s2("hello worldxxxxxxxxxxxxx"); // 确定需要多少空间,提前开好空间即可 s1.reserve(500); size_t old = s1.capacity(); cout << old << endl; for (size_t i = 0; i < 500; i++) { s1.push_back('x'); if (old != s1.capacity()) { cout << s1.capacity() << endl; old = s1.capacity(); } } } catch (const exception& e) { cout << e.what() << endl; } return 0; }

扩容倍数大致是1.5
reserve一般不会缩容,不会删除数据,最小缩到size()
resize

int main() { string s1("hello world"); cout << s1.size() << endl; cout << s1.capacity() << endl; cout << s1 << endl; // > capacity -》 扩容+尾插 //s1.resize(100); s1.resize(100, 'x'); cout << s1.size() << endl; cout << s1.capacity() << endl; cout << s1 << endl; // size < n < capacity -> 尾插 string s2("hello world"); cout << s2.size() << endl; cout << s2.capacity() << endl; cout << s2 << endl; s2.resize(12); cout << s2.size() << endl; cout << s2.capacity() << endl; // n < size -> 删除数据,保留前n个 string s3("hello world"); cout << s3.size() << endl; cout << s3.capacity() << endl; cout << s3 << endl; s3.resize(5); cout << s3.size() << endl; cout << s3.capacity() << endl; string s5; s5.resize(100, '#'); cout << s5 << endl; return 0; }

注意:
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不会改变容量大小。

  1. string类对象的访问及遍历操作
函数名称功能说明
operator []返回 pos 位置的字符,const string 类对象调用
begin+ endbegin 获取一个字符的迭代器 + end 获取最后一个字符下一个位置的迭代器
rbegin + rendbegin 获取一个字符的迭代器 + end 获取最后一个字符下一个位置的迭代器
范围 forC++11 支持更简洁的范围 for 的新遍历方式
[]
int main() { string s1("hello world"); cout << s1.size() << endl; cout << s1.length() << endl; for (size_t i = 0; i < s1.size(); i++) { cout << s1[i] << " "; //cout << s1.operator[](i) << " "; } cout << endl; return 0; }
// string给逆置一下 size_t begin = 0, end = s1.size() - 1; while (begin < end) { /*char tmp = s1[begin]; s1[begin] = s1[end]; s1[end] = tmp;*/ swap(s1[begin], s1[end]); ++begin; --end; } cout << s1 << endl;

iterator

// iterator用法像指针 string::iterator it = s1.begin(); while (it != s1.end()) { *it += 1; cout << *it << " "; ++it; } cout << endl; reverse(s1.begin(), s1.end()); cout << s1 << endl;

范围for

int main() { string s1("hello world"); const string s2("hello world"); s1[0] = 'x'; //s2[0] = 'x'; cout << s2[0] << endl; string::const_iterator it = s2.begin(); while (it != s2.end()) { //*it += 1; cout << *it << " "; ++it; } cout << endl; // yyds for (auto e : s1) { cout << e << " "; } cout << endl; return 0; }

反向迭代器

void func(const string& s) { //string::const_reverse_iterator it = s.rbegin(); auto it = s.rbegin(); while (it != s.rend()) { // *it = 'x'; cout << *it << " "; ++it; } cout << endl; auto it = s.crbegin(); while (it != s.crend()) { // *it = 'x'; cout << *it << " "; ++it; } cout << endl; } int main() { string s1("hello world"); // 遍历 // 1、下标+[] // 2、迭代器 // 3、范围for string::reverse_iterator it1 = s1.rbegin(); while (it1 != s1.rend()) { //*it1 = 'x'; cout << *it1 << " "; ++it1; } cout << endl; func(s1); return 0; }

at:抛异常,[]是程序报错

int main() { try { string s1("hello world"); cout << s1[11] << endl; //cout << s1[20] << endl; cout << s1.at(20) << endl; } catch (const exception& e) { cout << e.what() << endl; } return 0; }
  1. string类对象的修改操作
函数名称功能说明
push_back在字符串后尾插字符 c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串 str
c_str返回 C 格式字符串
find + npos从字符串 pos 位置开始往后找字符 c,返回该字符在字符串中的位置
rfind从字符串 pos 位置开始往前找字符 c,返回该字符在字符串中的位置
substr在 str 中从 pos 位置开始,截取 n 个字符,然后将其返回
// 增 +=(push_back/append)/insert // 删 erase // 查 [] // 改 []/迭代器 int main() { string s1("hello"); s1.push_back(' '); s1.append("world"); cout << s1 << endl; string s2 = "xxxx"; const string& s3 = "xxxx"; s2.append(++s1.begin(), --s1.end()); cout << s2 << endl; s1 += '!'; s1 += "xxxxx"; s1 += s2; cout << s1 << endl; return 0; }

insert、erase:

int main() { //string s1("hello world"); //s1.insert(5, "xxxx"); //cout << s1 << endl; ////s1.insert(0, 1, 'x'); //s1.insert(s1.begin(), 'y'); //cout << s1 << endl; string s1("hello world"); s1.insert(5, "xxxx"); cout << s1 << endl; s1.insert(0, 1, 'x'); s1.insert(s1.begin(), 'y'); cout << s1 << endl; s1.erase(5, 4); cout << s1 << endl; s1.erase(5); cout << s1 << endl; return 0; }

find、replace、s2.swap(s3)表示交换指针

int main() { string s1("hello world hello bit"); cout << s1 << endl; // 所有的空格替换为20% size_t pos = s1.find(' ', 0); while (pos != string::npos) { s1.replace(pos, 1, "20%"); // 效率很低,能不用就不要用了 pos = s1.find(' ', pos + 3); } cout << s1 << endl; string s2("hello world hello bit"); cout << s2 << endl; string s3; for (auto ch : s2) { if (ch == ' ') { s3 += "20%"; } else { s3 += ch; } } cout << s3 << endl; s2.swap(s3); cout << s2 << endl; return 0; }

c_str():

int main() { string filename("Test.cpp"); FILE* fout = fopen(filename.c_str(), "r"); char ch = fgetc(fout); while (ch != EOF) { cout << ch; ch = fgetc(fout); } return 0; }

substr():

int main() { string s1("Test.cpp"); string s2("Test.tar.zip"); size_t pos1 = s1.rfind('.'); if (pos1 != string::npos) { //string suff = s1.substr(pos1, s1.size() - pos1); string suff = s1.substr(pos1); cout << suff << endl; } size_t pos2 = s2.rfind('.'); if (pos2 != string::npos) { string suff = s2.substr(pos2); cout << suff << endl; } string str("https://legacy.cplusplus.com/reference/string/string/substr/"); string sub1, sub2, sub3; pos1 = str.find(':'); sub1 = str.substr(0, pos1 - 0); cout << sub1 << endl; pos2 = str.find('/', pos1+3); sub2 = str.substr(pos1 + 3, pos2 - (pos1 + 3)); cout << sub2 << endl; sub3 = str.substr(pos2 + 1); cout << sub3 << endl; return 0; }

find_first_of():

int main() { std::string str("Please, replace the vowels in this sentence by asterisks."); std::size_t found = str.find_first_not_of("abc"); while (found != std::string::npos) { str[found] = '*'; found = str.find_first_not_of("abc", found + 1); } std::cout << str << '\n'; string s1("xxxx"); string s2 = "xxxx"; return 0; }

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

函数功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>输入运算符重载
operator<<输出运算符重载
getline获取一行字符串
relational operators大小比较
  1. vs和g++下string结构的说明
    vs下string的结构
    注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
    string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:
  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty { // storage for small buffer or pointer to larger one value_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; };
  • 指向堆空间的指针,用来存储字符串。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 19:10:07

Miniconda安装PyTorch后无法调用GPU?常见问题排查指南

Miniconda安装PyTorch后无法调用GPU&#xff1f;常见问题排查指南 在深度学习项目中&#xff0c;你是否曾经历过这样的场景&#xff1a;满怀期待地启动训练脚本&#xff0c;结果发现模型仍在用CPU跑——明明有块高性能的NVIDIA显卡&#xff0c;torch.cuda.is_available() 却返回…

作者头像 李华
网站建设 2026/6/19 6:11:39

终极画中画体验:3分钟学会多任务高效工作神器

终极画中画体验&#xff1a;3分钟学会多任务高效工作神器 【免费下载链接】PiP-Tool PiP tool is a software to use the Picture in Picture mode on Windows. This feature allows you to watch content (video for example) in thumbnail format on the screen while contin…

作者头像 李华
网站建设 2026/6/17 7:37:39

NewGAN-Manager深度配置指南:3步打造专业级FM头像包

NewGAN-Manager深度配置指南&#xff1a;3步打造专业级FM头像包 【免费下载链接】NewGAN-Manager A tool to generate and manage xml configs for the Newgen Facepack. 项目地址: https://gitcode.com/gh_mirrors/ne/NewGAN-Manager 还在为Football Manager中那些显示…

作者头像 李华
网站建设 2026/6/14 16:27:58

Anaconda下载占用磁盘大?Miniconda-Python3.11仅需100MB

Miniconda-Python3.11&#xff1a;轻量启动&#xff0c;高效开发的现代 Python 环境构建之道 在如今动辄几十GB显存、数百个依赖包的AI项目中&#xff0c;一个看似微不足道却影响深远的问题正悄然浮现&#xff1a;你的Python环境&#xff0c;真的需要3GB才能跑起来吗&#xff1…

作者头像 李华
网站建设 2026/6/20 9:27:24

Path of Building终极指南:打造完美流放之路构筑

Path of Building终极指南&#xff1a;打造完美流放之路构筑 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/gh_mirrors/pat/PathOfBuilding 想要在《流放之路》中打造最强角色构筑吗&#xff1f;Path of Bu…

作者头像 李华
网站建设 2026/6/15 18:19:12

vGPU解锁技术深度解析:消费级显卡虚拟化突破指南

vGPU解锁技术深度解析&#xff1a;消费级显卡虚拟化突破指南 【免费下载链接】vgpu_unlock Unlock vGPU functionality for consumer grade GPUs. 项目地址: https://gitcode.com/gh_mirrors/vg/vgpu_unlock 在虚拟化技术高速发展的今天&#xff0c;NVIDIA的专业级vGPU功…

作者头像 李华