搭建网站用什么框架一个网站平台建设得多少钱
- 作者: 五速梦信息网
- 时间: 2026年03月21日 11:29
当前位置: 首页 > news >正文
搭建网站用什么框架,一个网站平台建设得多少钱,wordpress 命令执行,建站流程主要有哪些目录 前言 一、STL简介 二、string类 1.为什么学习string类 2.标准库中的string类 3.auto和范围for 4.迭代器 5.string类的常用接口说明 三、模拟实现 string类 前言 本文带大家入坑STL#xff0c;学习第一个容器string。 一、STL简介 在学习C数据结构和算法前#xff0c;我… 目录 前言 一、STL简介 二、string类 1.为什么学习string类 2.标准库中的string类 3.auto和范围for 4.迭代器 5.string类的常用接口说明 三、模拟实现 string类 前言 本文带大家入坑STL学习第一个容器string。 一、STL简介 在学习C数据结构和算法前我们需要先了解C的STL方便后续学习其他数据结构 1.什么是STL? STL(standard template libaray-标准模板库)是C标准库的重要组成部分不仅是一个可复用的组件库而且是一个包罗数据结构与算法的软件框架。 2.STL的版本 原始版本Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本本着开源精神他们声明允许 任何人任意运用、拷贝、修改、传播、商业使用这些代码无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。P. J. 版本由P. J. Plauger开发继承自HP版本被Windows Visual C采用不能公开或修改缺陷可读性比较低符号命名比较怪异。RW版本由Rouge Wage公司开发继承自HP版本被C Builder 采用不能公开或修改可读性一般。SGI版本由Silicon Graphics Computer SystemsInc公司开发继承自HP版本。被GCC(Linux)采用可移植性好可公开、修改甚至贩卖从命名风格和编程风格上看阅读性非常高。我们后面学习STL要阅读部分源代码主要参考的就是这个版本。 3.STL的六大组件 以上内容后续都会了解到总之我们要明白STL的重要性。STL在C的笔试和面试中占比很大在工作上更是“不懂STL不要说你会C”。STL是C中的优秀作品有了它的陪伴许多底层的数据结构以及算法都不需要自己重新造轮子站在前人的肩膀上健步如飞的快速开发。 二、string类 1.为什么学习string类 原字符串的缺陷C语言中字符串是以\0结尾的一些字符的集合为了操作方便C标准库中提供了一些str系列的库函数但是这些库函数与字符串是分离开的不太符合OOP的思想封装、继承和多态而且底层空间需要用户自己管理稍不留神可能还会越界访问。实用性在OJ中有关字符串的题目基本以string类的形式出现而且在常规工作中为了简单、方便、 快捷基本都使用string类很少有人去使用C库中的字符串操作函数。 2.标准库中的string类 在使用string类时必须包含#include string头文件平时学习可使用using namespace std;展开命名空间方便使用 #include iostream #include string using namespace std;int main() {string s1(hello world);//定义对象s1并初始化cout s1 endl;//打印输出return 0; } 简单说明下命名空间std 与 头文件 还有 STL 之间的联系 命名空间std 是C标准库的命名空间也就是C编程的重要组成部分它不仅包含了STL的所有组件也包括了更多的东西。例如输入输出流cin,cout、容器string,vector,list,map等、迭代器、智能指针、内存管理工具、算法等所以我们平时使用库函数和容器等如果不使用using namespace std;展开这个命名空间的话就需要在前面指定命名空间std::。头文件的包含比如iostream和strintg你实际上是在告诉编译器你想要使用该头文件中定义的功能这些功能都是 std 这个命名空间的一部分因此可以说我们是通过包含不同的头文件来解锁和访问 std 命名空间中不同部分的内容。不过需要注意的是C语言本身不仅仅是由 std 命名空间组成。C的核心内容还包括语言语法、基础数据类型、内存模型和管理、面向对象编程特性、模版和泛型编程、异常处理。切记关键字不是 std 中定义的C关键字是语言本身的一部分它们不是由标准库提供的而是直接由编译器识别的。 继续了解string类 其实严格意义上string不属于容器在下图容器分类中就没有看到string 这是因为string在STL之前就已经有了因为在设计上与STL中容器很相似因此就有 串 这么一个数据结构后面使用方法中就可以看出 string 设计的方法非常冗余因为要照顾旧方法同时又要融入STL。 基础串 string类其实是一个类模版它的原模版就叫 basic_string基础串 在基础串模版中后两个参数有默认的模版参数string 的定义中给基础串的第一个参数传递了 char 并进行了重命名所以我们创建 string 模版类时没有给定模版参数 当然除了经常使用的 string 类外还有另外两个不同的string类 这两个不同的string类一个是一个字符占2个字节另一个是4个字节这里大小不同的原因是因为编码不同。编码在下文学习完string的使用后会讲到。这里主要是讲为什么要搞一个基础串的模版原因就在这里。 不管怎样我们最常用的还是前面的string主要因为它兼容的编码多。 深入学习string前先学习两个语法糖 3.auto和范围for
- auto关键字 #include iostream using namespace std;int main() {int a 1;auto b a;//根据表达式右边自动推导出b的类型cout b endl;cout sizeof(b) endl;return 0; } 运行结果 auto使用注意事项C11 在早期C/C中auto的含义是使用auto修饰的变量是具有自动存储器的局部变量后来这个不重要了。C11中标准委员会变废为宝赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一个新的类型指示符来指示编译器auto声明的变量必须由编译器在编译时期推导而得。用auto声明指针类型时用auto和auto*没有任何区别但用auto声明引用类型时则必须加当在同一行声明多个变量时这些变量必须是相同的类型否则编译器将会报错因为编译器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量。auto不能作为函数的参数可以做返回值c11支持但是建议谨慎使用。补充c20开始支持 auto 作为函数参数类型auto不能直接用来声明数组 例如 1推导指针类型 #include iostream using namespace std;int main() {//自动推导指针int a 10;auto b a;cout b endl a endl;return 0; } 运行结果 2推导引用类型 #include iostream using namespace std;int main() {//引用类型推导int a 10;int b a;auto c b;cout a endl;cout c endl endl;//不加推导出来的不是引用类型而是原数据类型auto d b;cout d endl;return 0; } 运行结果 3可做函数返回值但不能做参数 //auto做返回值 auto func1() {int a 10;return a; }//auto做参数 //报错error C3533: 参数不能为包含“auto”的类型 //int func2(auto x) //{ // int a x; // return a; //} 但auto作为返回值有时候会是个坑因为如果代码复杂维护时会导致无法快速判断该函数返回值写了函数注释还好没写就会大大增加代码维护成本所以慎用。 2.范围for 语法 for(类型 e : 容器) { //每循环一次e自动指向下一个数据 //直到容器遍历完成 } #include iostream #include string using namespace std;int main() {//范围for用于遍历容器string s1(hello world);for (char c : s1){cout c ;}cout endl;//可配合auto使用//如果想改变容器内容需要使用引用类型for (auto c : s1){c;}cout s1 endl;return 0; } 运行结果 范围for用处总结 对于一个有范围的集合而言由程序员来说明循环的范围是多余的有时候还会容易犯错误。因此 C11 中引入了基于范围的for循环。for循环后的括号由冒号“ ”分为两部分第一部分是范围内用于迭代的变量第二部分则表示被迭代的范围自动迭代自动取数据自动判断结束。范围for可以作用到数组和容器对象上进行遍历范围for的底层很简单容器遍历实际就是替换为迭代器这个从汇编层也可以看到。 4.迭代器 迭代器STL六大组件之一关于迭代器的介绍C迭代器是一种用于遍历容器如数组、链表、向量等中元素的工具。它们提供了统一的接口使得不同类型的容器可以以相似的方式进行访问和操作。我们现阶段可以先理解迭代器为一种指针但本质上不是指针我们先学会使用 常见迭代器 iterator常量迭代器 const_iterator反向迭代器 reverse_iterator常量反向迭代器 const_reverse_iterator 声明迭代器时一般是 std::容器名(如果是模版需要模版参数):: iterator 对象名。 展开了命名空间std就可省略因为不同容器迭代器底层实现不同因此需要指定容器 例如利用迭代器遍历string #include iostream #include string using namespace std;int main() {string s1(hello world);//迭代器遍历string::iterator it s1.begin();while (it ! s1.end()){cout *it ;it;}cout endl;return 0; } 运行结果 解释 首先 string类的接口 begin()和end() 我们发现它们的返回值就是迭代器它们的作用就是返回容器的头部与尾部的迭代器对于string来说end()指向的就是\0begin()指向的就是下标为0的字符。 在使用上用法和指针相似 使用*运算符解引用迭代器以访问它所指向的元素。使用-运算符访问指向的对象的成员如果该对象是一个类或结构体。可以使用或–来前进或后退迭代器。 注意判断迭代器是否走到容器的结尾是使用 ! 容器.end() 来判断而不是其他关系判断另外一般使用while循环遍历for循环虽然也行但写法上相较复杂点。 迭代器的全部接口 r开头的就是支持反向迭代器c开头的就是就是常量迭代器但是前面我们注意到了begin 和 end 都有重载const版本的在 rbegin 和 rend 中一样都有重载 const 版本的注意这种成员函数重载是根据 const 区分的不是参数。因此我们一般不使用c开头的以及cr开头的迭代器接口。 rend() 指向的是第一个元素前一个位置rbegin() 指向的是最后一个元素的位置对于string来说就是\0前一个字符因为倒着遍历还是从 rbegin 开始一直到 ! rend() 结束因此这种安排合理 剩余三种迭代器遍历演示 #include iostream #include string using namespace std;int main() {//1.反向迭代器string s1(hello world);string::reverse_iterator rit s1.rbegin();while (rit ! s1.rend()){cout *rit ;rit;}cout endl;//2.const迭代器const string s2(hello world);//string::const_iterator cit s1.begin();auto cit s2.begin();//使用前面所学的auto自动识别类型更加方便while (cit ! s2.end()){cout *cit ;cit;}cout endl;//3.const反向迭代器auto crit s2.rbegin();while (crit ! s2.rend()){cout *crit ;crit;}cout endl;return 0; } 运行结果 需要注意的是反向迭代器虽是倒着遍历但依旧是使用使迭代器指向下一个元素。因为对于反向迭代器来说它正方向就是从右往左。 另外迭代器与指针类似当然也可以修改非const容器对象的内容 #include iostream #include string using namespace std;int main() {string s1(hello world);auto it s1.begin();while (it ! s1.end()){(*it);it;}cout s1 endl;return 0; } 运行结果 小结 迭代器的是所有的STL容器通用的一种元素访问方式不同的容器迭代器底层会有些不同但是用法是一样的因此学好迭代器很重要 5.string类的常用接口说明 强调C为了适配C语言因此 string 类对象的末尾也是存在 \0 的 1.string类的常见构造 上图中 1就是不传参的默认构造2就是拷贝构造3从string对象 str 的 pos(下标) 位置开始拷贝 len默认 nops个字符进行构造4使用字符串进行构造5使用字符串 s 的前 n 个字符进行构造6用 n 个相同字符 c 进行构造7使用迭代器进行构造 下面演示一下34567 #include iostream #include string using namespace std;int main() {string s1(hello world);//使用字符串初始化构造cout s1 s1 endl;string s2(s1, 6, 5);//利用string对象的下标长度进行构造cout s2 s2 endl;string s3(xxxxxxxxxxxx, 4);//使用字符串的前4个字符进行构造cout s3 s3 endl;string s4(5, y);//用n个相同字符进行构造cout s4 s4 endl;string s5(s1.begin(), s1.end() - 5);//利用迭代器进行构造cout s2 s5 endl;return 0; } 运行结果 赋值运算符重载 演示 #include iostream #include string using namespace std;int main() {string s1(111);string s2(222);//(1)s1 s2;cout s1 endl;//(2)s1 333;cout s1 endl;//(3)s1 ;cout s1 endl;return 0; } 运行结果 补充npos和析构 1npos npos 是 string 类的一个静态成员变量无符号整形并且等于-1因此就是整形的最大值2进制位全是1常用作缺省参数表示最大值。 2string 类的析构 类的析构函数会在对象作用域结束时自动调用用于销毁对象 2.string类对象的容量操作 函数名称简要功能说明size返回字符串有效字符长度length返回字符串有效字符长度 max_size 返回字符串可以达到的最大长度。resize将有效字符的个数该成n个多出的空间用字符c填充capacity返回空间总大小reserve为字符串预开辟空间clear清空有效字符empty检测字符串释放为空串是返回true否则返回falseshrink_to_fit缩容减小字符串容量以适应其大小 1size和length size和length的功能相同都是返回字符串有效字符个数而这样设计的原因是历史原因导致的主要就是STL出来之前string已经存在了。为了保留原string接口同时为了和STL其余容器保持通用性因此设计了size其余STL容器都是size返回有效元素个数。对于string我们平时也基本是使用size而不是length。 演示 2max_size 这个用处不大编译器也开不了这么大的空间。 演示64位 3capacity 返回空间容量无需多言 演示 4resize 将字符串大小调整为 n 个字符的长度那么这里就有3中情况 如果 n 小于当前字符串长度则当前值将缩短为其前 n 个字符并删除第 n 个字符以外的字符。如果 n 大于当前字符串长度却又小于当前空间容量则将当前字符串大小调整为 n指定了c则新元素将初始化为 c 未指定则初始化为\0如果 n 大于当前空间容量则需要扩容然后初始化新元素新元素处理同上 演示 #include iostream #include string using namespace std;int main() {string s1(11111111111111111111);cout s1 endl;cout size: s1.size() endl;cout capacity: s1.capacity() endl;cout endl;//resize//1.n sizes1.resize(10);cout s1 endl;cout size: s1.size() endl;cout capacity: s1.capacity() endl;cout endl;//2.size n capacitys1.resize(25,x);cout s1 endl;cout size: s1.size() endl;cout capacity: s1.capacity() endl;cout endl;//3.n capacitys1.resize(40, y);cout s1 endl;cout size: s1.size() endl;cout capacity: s1.capacity() endl;cout endl;return 0; } 运行结果 5reserve 更改空间容量如果 n 大于当前字符串容量则该函数会导致容器将其容量增加到 n 个字符或更大。此函数对字符串长度没有影响也无法更改其内容。因此reserve不能缩小容量也就是 n 小于当前字符串容量没有什么实际效果。适用场景提前知道大概需要多少空间提前开辟可以避免多次扩容提升效率。 演示 拓展 我们观察string类对象每次扩容的大小 #include iostream #include string using namespace std;int main() {string s1;size_t old s1.capacity();cout capacity: old endl;for (size_t i 0; i 100; i){s1 x;if (s1.capacity() ! old){cout capacity: s1.capacity() endl;old s1.capacity();}}return 0; } 运行结果 我们发现除了第一次到第二次是2倍扩容以外31以后就是 1.5 倍扩容了。首先这个底层扩容倍率每个平台是不一定一样的以上是vs2022的结果。然后为啥第一次不是1.5倍扩容的原因string 底层还存在一个类似 char buff[16] 大小的字符数组如果数据小于16的话就会存在这个数组里面大于16就储存在堆上开的空间中。这样做是为了避免存储数据小时频繁开辟空间。所以第一次的容量 15 不算是扩容。 我们可以通过计算空间大小验证一下32位 28的由来底层字符串指针 4 字节、底层 size 和 capacity 记录大小和容量的无符号整形一共占 8 字节、剩下的 16 个字节就是 char buff[16] 数组。 我们在调试窗口中也能观察到该数组 5clear 清空有效字符对应字符串来说就是将\0移动到第一位 6empty 判空为空返回ture(1)反之返回false(0)。 7shrink_to_fit 缩容将容量缩小与有效字符一样大的空间注意该函数不是任意情况下都会进行缩容而是当capacity 与 size 相差过大时才会缩容。 3.string类对象的访问接口 1operator[ ] 运算符重载函数返回对应下标的引用越界会直接报错最常用的元素访问接口 演示 #include iostream #include string using namespace std;int main() {string s1(hello world);cout s1[4] endl;s1[4] x;//因为返回的是引用类型因此修改可直接影响原对象cout s1[4] endl;return 0; } 运行结果 配合 size()接口可以实现遍历string类对象 #include iostream #include string using namespace std;int main() {string s1(hello world);for (size_t i 0; i s1.size(); i){cout s1[i] ;}cout endl;return 0; }运行结果 2at at 功能大致与 operator[ ] 相同区别是 at 访问失败会抛出异常而 [ ] 是直接断言报错 演示关于捕获异常的知识我会在后续篇章中单独讲解 3back 和 front back 和 front 分别是返回字符串第一个字符和最后一个字符因为这些 [ ] 也可以轻松做到所以这两个接口用的不多访问元素用的最多的就是 [ ]。 演示 4.string类对象的修改操作 函数名称功能说明 operator 在字符串后追加字符或字符串 append 在字符串后追加一个字符串 push_back 尾插一个字符 assign 为字符串分配一个新值替换其当前内容 insert 在指定位置前插入字符或字符串 erase 删除指定位置字符 replace 替换指定位置字符 swap 交换两个字符串 pop_back 尾删一个字符 1operator 我们可以直接尾插一个string类对象或者一个字符串或者一个字符 是字符串尾插中运用最多的接口 演示 除了以外string 也重载了 运算符区别就是不会修改本身返回值为 的结果 2push_back 尾插一个字符 演示 3append append 重载了许多函数功能都是尾插一段字符串 1尾插一个 string 类对象2从待尾插 string 对象的 subpos 位置开始尾插 sublen 个字符3尾插一段字符串4尾插一段字符串的前 n 个字符5尾插 n 个相同的字符 c6以迭代器的方式尾插一段字符串 演示 #include iostream #include string using namespace std;int main() {string s1(111);string s2(xxxx);string s3(hello world);//(1)s1.append(s2);cout s1 endl;//(2)s1.append(s3, 0, 5);cout s1 endl;//(3)s1.append(world);cout s1 endl;//(4)s1.append(yyyyyyyyyyy, 3);cout s1 endl;//(5)s1.append(2, a);cout s1 endl;//(6)s1.append(s3.begin() 5, s3.end());cout s1 endl;return 0; } 运行结果 4insert insert 支持头插以及中间指定位置之前插入元素重载了很多函数类比构造和append函数其实不难看出每种重载函数的用法以下不一一列举了提醒insert 进行头插和中间插入时需要挪动数据因此效率低下不建议多次使用。 演示 #include iostream #include string using namespace std;int main() {string s1(111);string s2(22);string s3(xxxxxx);string s4(orld);//(1)s1.insert(0, s2);cout s1 endl;//(2)s1.insert(3, s3, 0, 2);cout s1 endl;//(3)s1.insert(s1.size(), hello);cout s1 endl;//(4)s1.insert(0, yyyyyyyyyyyyy, 3);cout s1 endl;//(5)s1.insert(0, 4, m);cout s1 endl;//(6)s1.insert(s1.end(), w);cout s1 endl;//(7)s1.insert(s1.end(), s4.begin(), s4.end());cout s1 endl;return 0; } 运行结果 5erase erase 用于删除字符 1缺省参数 0 和 nposnpos前面说过是整形最大值也就是说什么都不传默认全部删除效果和 clear 一样传参则按照指定位置大小删除。2删除迭代器位置的字符3删除迭代器区间的字符 演示 #include iostream #include string using namespace std;int main() {string s1(I want to be a C master);//(1)s1.erase(0, 1);cout s1 endl;//(2)s1.erase(s1.begin());cout s1 endl;//(3)s1.erase(s1.begin(), s1.begin() 5);cout s1 endl;//(1)s1.erase();cout s1 endl;return 0; } 运行结果 6assign 该函数主要功能是对字符串进行重新赋值相比重载的赋值运算符功能上有重合虽然assign更灵活但用的更多的还是重载的赋值运算符函数。 演示根据前面函数的参数很容易判断每种重载函数的功能因此不再详细演示 7replace replace 主要功能就是替换也提供了一大堆重载函数我们不用一个个去记忆需要的时候查阅就行前面我们已经判断了很多重载函数的功能根据参数就大致能判断出每种重载函数的用法。另外在替换过程中如果是平替替换与被替换字符数相等则效率高如果不是平替少替多多替少替换次数多了时效率就会很低因此除了平替或者替换次数少不建议经常使用 演示只演示一个 8pop_back 尾删一个字符 演示 9swap 关于 swapstring类提供了一个还有一个全局的算法库里面也有一个这么设计的原因是什么 原因 第一个 swap 是 string类 的成员函数例如两个string对象s1、s2使用 s1.swap(s2) 即可调用到该函数完成交换该交换是直接交换两个字符串的地址因此效率高。而我们平时习惯性写成 swap(s1,s2)这样就会调用到算法库里的swap也就是第三个swap该swap是一个函数模版其内部对于 string 对象来说是深拷贝深拷贝效率没有直接交换两个字符串地址效率高。因此为了避免调用到第三个swap就创造了第二个全局的swap函数。第二个 swap 函数内部就是调用第一个swap直接交换两字符串地址因此效率比第三个swap高第三个swap是函数模版对于函数模版来说有现成的就会直接使用现成的不会再实例化一份。因此写成 swap(s1,s2)不会调用到第三个swap而是调用第二个swap。关于这样的设计其它容器也是如此都是为了方便调用到成员函数的swap。 5.string类对象的其它常见操作 函数名称函数功能c_str将string类对象的数据以C语言字符串的格式返回copy、substr相比copysubstr用的更多用于截取当前字符串的子串find系列用于查找字符或字符串 关系运算符重载、compare 因为string重载了关系运算符所以一般很少使用compare判断两字符串关系operator、operator重载的流插入、流提取运算符getline从输入流中读取字符 1c_str 获取C语言格式的字符串因为C兼容C语言所以有时候需要混合编程但是C语言中关于字符串的库函数是不支持string类对象的因此使用该函数就能解决这些问题 演示 #define _CRT_SECURE_NO_WARNINGS 1 #include iostream #include string using namespace std;int main() {//C使用C语言的文件操作string s1(test.cpp);FILE pf fopen(s1.c_str(), r);//c_str返回C格式的字符串char ch fgetc(pf);while (ch ! EOF){cout ch;ch fgetc(pf);}cout endl;return 0; } 运行结果 2substr 返回当前字符串 pos 位置开始len 长的子串。因为都有缺省参数所以默认返回整个字符串 演示 #include iostream #include string using namespace std;int main() {string s1(*hello world*);string s2 s1.substr(4, 11);cout s2 endl;return 0; } 运行结果 3find系列 1. find 第一个参数 str、s、c 就是需要查找的字符串或字符参数 pos 是查找的起始位置默认0则从头开始找第三个重载函数的参数 n 是指定需查找的字符串 s 的长度find 查找成功会返回匹配的第一个字符的下标查找失败则返回 string::npos 演示 #include iostream #include string using namespace std;int main() {//将下面字符串的空格全部替换为 # 号string s1(There are two needles in this haystack with needles.);size_t pos s1.find( );while (pos ! string::npos){s1[pos] #;pos s1.find( , pos 1);}cout s1 endl;return 0; } 运行结果 2.rfind rfind 就是倒着找其他的和 find 一样适用于找后缀的场景 演示 #include iostream #include string using namespace std;int main() {//指出下面文件的后缀名string s1(test.cpp.zip);size_t pos s1.rfind(.);cout s1.substr(pos) endl;return 0; } 运行结果 3.find_first_of 作用在字符串中搜索与其参数中指定的任何字符匹配的第一个字符简单点说find是查找单一字符或字符串find_first_of是查找一个集合只要查找的字符串中出现了这个集合中的字符那么它就会返回该下标成功返回下标失败返回 string::npos 演示 #include iostream #include string using namespace std;int main() {//屏蔽5个元音字母string s1(qwertyuiopasdfghjklzxcvbnm);size_t pos s1.find_first_of(aeiou);while (pos ! string::npos){s1[pos] *;pos s1.find_first_of(aeiou, pos 1);}cout s1 endl;return 0; } 运行结果 4.find_first_not_of 该函数与 find_first_not_of 相反它是找出所有不在匹配串中的字符位置 演示 #include iostream #include string using namespace std;int main() {//屏蔽5个元音字母以外的字母string s1(qwertyuiopasdfghjklzxcvbnm);size_t pos s1.find_first_not_of(aeiou);while (pos ! string::npos){s1[pos] ;pos s1.find_first_not_of(aeiou, pos 1);}cout s1 endl;return 0; } 运行结果 5.find_last_of 与 find_last_not_of 相比 find_first_of 和 find_first_not_of区别就是倒着找这里不再赘述和演示 4关系运算符重载 注意是全局函数不是成员函数模拟时会详细说明 5operator、operator 模拟时会详细说明 6getline 解决流提取时无法读取空格和换行符等问题参数 delim 是自定义读取结束符不传参默认读到换行符结束 你是否遇到以下困扰cin流提取时遇到空格或者换行符会自动截断导致赋值不完整。 而getline就是专门解决这个问题的 三、模拟实现 string类 了解完string类的使用接下来就是自己模拟实现出string类 为了避免太复杂我们不使用模版实现还是按照声明和定义分离的方式来实现string类模拟实现的意义是让我们对string的使用更加深刻不是完全的模拟实现主要是对常用的接口的模拟实现。 1.string.h 头文件 #pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include iostream #include cassert using namespace std;//为避免与std中的string冲突因此定义一个命名空间分隔 namespace txp {class string{public://定义迭代器using iterator char;using const_iterator const char;//声明构造拷贝构造string(const char str );string(const string s);//赋值运算符重载string operator(string s);//声明析构函数~string();//对于一些简短的函数直接在头文件中定义较长的函数则放到定义文件中//定义c_str成员函数const char* c_str() const{return _str;}//定义size成员函数size_t size() const{return _size;}//定义重载运算符[]char operator{assert(i _size);return _str[i];}//const版本 []const char operator const{assert(i _size);return _str[i];}//定义迭代器接口beginiterator begin(){return _str;}//迭代器接口enditerator end(){return _str _size;}//const版本的beginconst_iterator begin() const{return _str;}//const版本的endconst_iterator end() const{return _str _size;}//定义clear成员函数void clear(){_str[0] \n;_size 0;}//声明reserve函数void reserve(size_t n);//声明push_back函数void push_back(char ch);//声明append函数void append(const char* str);//声明运算符重载函数string operator(char ch);//声明第二个版本的string operator(const char* str);//声明insert成员函数void insert(size_t pos, char ch);//声明重载的insert成员函数void insert(size_t pos, const char* str);//声明erase成员函数void erase(size_t pos, size_t len npos);//声明find成员函数size_t find(char ch, size_t pos 0) const;size_t find(const char* str, size_t pos 0) const;//声明substr成员函数string substr(size_t pos 0, size_t len npos) const;//声明swap成员函数void swap(string str);private://底层结构char* _str nullptr;size_t _size 0;size_t _capacity 0;public://声明静态成员变量nposstatic const size_t npos;};//声明关系运算符重载函数bool operator(const string s1, const string s2);bool operator!(const string s1, const string s2);bool operator(const string s1, const string s2);bool operator(const string s1, const string s2);bool operator(const string s1, const string s2);bool operator(const string s1, const string s2);//声明流插入、流提取运算符重载函数以及getline函数ostream operator(ostream os, const string str);istream operator(istream is, string str);istream getline(istream is, string str, char delim \n);//声明全局的swap函数void swap(string s1, string s2); } 2.string.cpp 文件 因函数之间存在复用关系因此大家直接看注释吧 #include string.hnamespace txp {//定义全局静态变量nposconst size_t string::npos -1;//默认构造注意只能在声明处给缺省值因此定义时没有写缺省值string::string(const char* str):_size(strlen(str)){_capacity _size;_str new char[_size 1];//多开辟一个空间用于存储\0strcpy(_str, str);}//拷贝构造//1.传统写法自己开空间自己拷贝/string::string(const string s){_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity;}///2.现代写法利用构造开空间利用swap拷贝string::string(const string s){string tmp(s._str);//创建临时对象tmp用于拷贝s的_str进行构造swap(tmp);//交换后this指向的对象就是拷贝构造出来的对象而tmp出了函数就会被析构}//赋值运算符重载//1.传统写法/*string string::operator(const string s){if (this ! s){delete[] _str;_str new char[s._capacity 1];strcpy(_str, s._str);_capacity s._capacity;_size s._size;}return this;}///2.现代写法string string::operator(string s)//利用传值传参进行拷贝构造{swap(s);//再进行交换原this指向的空间就由s析构带走了return this;}//注意现代写法没有效率提升只是更简洁了本质是一种复用//析构string::~string(){delete[] _str;_str nullptr;_size _capacity 0;}//reserve开空间只考虑扩容的情况void string::reserve(size_t n){if (n _capacity){char tmp new char[n 1];strcpy(tmp, _str);delete[] _str;_str tmp;_capacity n;}}//尾插字符void string::push_back(char ch){/*if (_size _capacity){reserve(_capacity 0 ? 4 : _capacity * 2);}_str[_size] ch;/insert(_size, ch);//当我们实现insert后可以直接复用来实现push_back的效果}//尾插字符串void string::append(const char str){/*size_t len strlen(str);if ((_size len) _capacity){size_t newCapacity 2 * _capacity;if ((len _size) newCapacity){newCapacity len _size;}reserve(newCapacity);}strcpy(_str _size, str);_size len;*/insert(_size, str);//可直接复用insert}//重载运算符string string::operator(char ch){push_back(ch);//复用push_back即可return this;}//重载版本string string::operator(const char str){append(str);//复用append即可return *this;}//insert插入void string::insert(size_t pos, char ch){assert(pos _size);//需扩容时按照2倍扩容if (_size _capacity){reserve(_capacity 0 ? 4 : _capacity * 2);}//挪动数据size_t end _size 1;while (end pos){_str[end] _str[end - 1];–end;}//插入_str[pos] ch;_size;}//insert重载void string::insert(size_t pos, const char* str){assert(pos _size);//由于不确定插入的字符串大小因此扩容时需进行2次判断size_t len strlen(str);if ((_size len) _capacity){size_t newCapacity 2 * _capacity;if ((len _size) newCapacity)//2倍扩容不够就需要多少开多少{newCapacity len _size;}reserve(newCapacity);}//挪动数据size_t end _size len;//对于字符串来说停止条件不能写成endpos会导致越界poslen是最后一次挪动的位置//因此要保证end poslen时继续挪动所以停止条件为end (poslen-1)while (end (pos len - 1)){_str[end] _str[end - len];–end;}//插入for (size_t i 0; i len; i){_str[pos i] str[i];}_size len;}//删除void string::erase(size_t pos, size_t len){assert(pos _size);//第一种情况要删除的字符数大于剩余的字符直接挪动\0所在位置即可if (len (_size - pos)){_str[pos] \0;_size pos;}else//剩下的情况就是要手动挪动剩余数据了{size_t end pos len;while (end _size){_str[end - len] _str[end];//从后向前挪end;}_size - len;}}//查找size_t string::find(char ch, size_t pos) const{assert(pos _size);for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;}//字符串查找算法有很多我们直接使用C库里的函数strstrsize_t string::find(const char* str, size_t pos) const{assert(pos _size);const char* ptr strstr(_str pos, str);if (ptr nullptr){return npos;}else{return ptr - _str;}}//取子串string string::substr(size_t pos, size_t len) const{assert(pos _size);//len大于剩余串长度则直接取到结尾if (len (_size - pos)){len _size - pos;}txp::string sub;sub.reserve(len);for (size_t i 0; i len; i){sub _str[pos i];}return sub;}//交换void string::swap(string str){//调用算法库中的swap即可std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}//关系运算符重载bool operator(const string s1, const string s2){return strcmp(s1.c_str(), s2.c_str()) 0;//直接利用C的库函数}bool operator!(const string s1, const string s2){return !(s1 s2);//复用}bool operator(const string s1, const string s2){return strcmp(s1.c_str(), s2.c_str()) 0;//利用C库}bool operator(const string s1, const string s2){return strcmp(s1.c_str(), s2.c_str()) 0;//利用C库}bool operator(const string s1, const string s2){return s1 s2 || s1 s2;//复用和}bool operator(const string s1, const string s2){return s1 s2 || s1 s2;//复用和}//流插入ostream operator(ostream os, const string str){for (size_t i 0; i str.size(); i){os str[i];}return os;}//流提取istream operator(istream is, string str){str.clear();//先清空数据int i 0;char buff[256];//为避免多次扩容选择创建一个buff数组char ch;//传统的流提取会忽略掉空格和换行符怎么解决呢ch is.get();//get为istream类对象的一个接口可以读取任意字符while (ch ! ch ! \n){buff[i] ch;//当buff数组存满时就到strif (i 255){buff[i] \0;i 0;str buff;}ch is.get();}//如果buff中还有剩余字符未处理if (i 0){buff[i] \0;str buff;}return is;}//定义getline函数istream getline(istream is, string str, char delim){str.clear();int i 0;char buff[256];char ch;ch is.get();while (ch ! delim)//与流提取的差别就是这里delim控制结束符{buff[i] ch;if (i 255){buff[i] \0;i 0;str buff;}ch is.get();}if (i 0){buff[i] \0;str buff;}return is;}//全局交换void swap(string s1, string s2){//调用成员函数的swap即可s1.swap(s2);} } 总结 以上就是本文的全部内容感谢支持祝大家新年快乐
- 上一篇: 搭建网站用什么框架环球广贸WordPress
- 下一篇: 搭建网站原理网站识别手机电脑自动跳转
相关文章
-
搭建网站用什么框架环球广贸WordPress
搭建网站用什么框架环球广贸WordPress
- 技术栈
- 2026年03月21日
-
搭建网站是什么成都网站建设 推广行
搭建网站是什么成都网站建设 推广行
- 技术栈
- 2026年03月21日
-
搭建网站内链系统专做专业课视频的网站
搭建网站内链系统专做专业课视频的网站
- 技术栈
- 2026年03月21日
-
搭建网站原理网站识别手机电脑自动跳转
搭建网站原理网站识别手机电脑自动跳转
- 技术栈
- 2026年03月21日
-
搭建一个企业网站需要多少钱手机网站网站开发流程图
搭建一个企业网站需要多少钱手机网站网站开发流程图
- 技术栈
- 2026年03月21日
-
搭建一个网站的流程湖北省交通建设监理协会网站
搭建一个网站的流程湖北省交通建设监理协会网站
- 技术栈
- 2026年03月21日






