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

网站seo的主要优化内容轻云服务器 安装wordpress

网站seo的主要优化内容,轻云服务器 安装wordpress,免费wap建站的网址是什么了,建网站的尺寸公主请阅 1. 为什么学习string类#xff1f;2. string类的常用接口2.1 string类对象的常见构造2.1.1 string 2.2 operator[]2.3 迭代器2.4 auto自动推导数据类型2.5 范围for2.6 迭代器第二层2.7 size和length获取字符串的长度2.8 max_size 获取这个字符串能设置的最大长度2.9 … 公主请阅 1. 为什么学习string类2. string类的常用接口2.1 string类对象的常见构造2.1.1 string 2.2 operator[]2.3 迭代器2.4 auto自动推导数据类型2.5 范围for2.6 迭代器第二层2.7 size和length获取字符串的长度2.8 max_size 获取这个字符串能设置的最大长度2.9 capacity2.10 clear2.11 operator以及at2.12 push_back尾插一个字符2.13 append2.14 operator2.15 reserve 提前开空间避免扩容的操作2.16 题目1仅仅反转字母2.17 题目二字符串相加2.18 resize2.18.1 使用 std::vector 的 resize() 函数 2.19 insert2.20 erase2.21 repalce 替换e2.22 rfind2.23 find_first_of2.24 find_last_not_off2.25 substr2.26 to_string2.27总结 3. string类的模拟实现3.1 string的模拟实现3.2 三个swap函数3.21 区别 1. 为什么学习string类 C语言中字符串是以’\0’结尾的一些字符的集合为了操作方便C标准库中提供了一些str系列的库函数但是这些库函数与字符串是分离开的不太符合OOP的思想而且底层空间需要用户自己管理稍不留神可能还会越界访问。 在OJ中有关字符串的题目基本以string类的形式出现而且在常规工作中为了简单、方便、快捷基本都使用string类很少有人去使用C库中的字符串操作函数 string是一个管理字符数组的类 2. string类的常用接口 string是一个管理字符的类所以里面有很多关于字符的成员函数 2.1 string类对象的常见构造 2.1.1 string https://legacy.cplusplus.com/reference/string/string/string/ #includestring int main() {//std::string s1;string s1(111111222222);//111111222222string s2(11111111111111,3);// 111string s3(100,x);//用100个x进行初始化操作string s4(s1, 4,3);//拷贝s2第四个位置的三个字符//112string s5(s1, 4);//拷贝s2第四个位置到结束//11222222//如果我们给的字符串太短了的话有多少拷贝多少直到结束cout s1 endl;cout s2 endl;cout s3 endl;cout s4 endl;cout s5 endl;return 0; }2.2 operator[] 我们现在想进行字符串的遍历那么我们就可以使用string类里面的operator[] operator[]可以访问数组的第pos位置的元素越界就会直接报错的 我们的这个operator是一个既可以读也可以写的接口两个const 所以这个重载函数提供了两个接口如果我们是const成员对象的话我们提供const operator[],返回pos位置的const引用那么我们就不能进行修改的操作那么这里就是只能进行读的 如果是普通对象的话我们就调用普通对象的版本吗获取pios位置的字符的引用别名可以读到这个数据也能写这个数据 对于我们之前学到的普通对象取地址重载我们对const对象和普通对象都有普通对象返回的是Date* const对象返回的是const Date * class string { public:char operator[](size_t pos){assert(pos _size);} private:char* _str;size_t _size;size_t _capacity; }; 第一种方法就是使用operator[]进行数组元素的遍历操作 通过下标[]进行数组元素的遍历操作 int main() {//我们现在想进行字符串的遍历那么我们就可以使用string类里面的operator[]//operator[]可以访问数组的第pos位置的元素越界就会直接报错的string s1(hello world);for (size_t i 0; i s1.size(); i){cout s1[i];//直接访问第i个位置的字符}return 0; } /2.3 迭代器 第二种方法就是我们通过迭代器进行遍历的实现操作迭代器可以理解为像指针一样的东西 int main() {string s1(hello world);string::iterator it1 s1.begin();//用这个类型定义一个对象//我们这里的话begin访问的是h这个字符end指向的是\0//这里将begin给到了it1while (it1 ! s1.end())//end是最后一个数据的下一个位置{//下面的用法非常像指针但是不是指针cout *it1 ;it1;}return 0; } //这个s1是一个类对象然后我们利用这个string类里面的运算符我们这里视同begin获取第一个字符给到it1 然后这个while循环的条件是it1走到\0之前就停止 然后我们在循环里面就能将这个it1所指向的对象进行打印操作 但是我们需要在it1前面加上*和指针差不多但是不是指针 下标[]很爽但是我们为什么要学迭代器呢迭代器写起来很麻烦 日常的通过下标[]访问很方便但是这个不是通用的访问方式这个只适合string和vector结构不适合后面的结构string和vector结构底层是连续的空间才回去重载operator[] 下标[]不是通用的方式但是迭代器是所有容器通用的方式 我们这个迭代器的遍历这里最好用≠来进行遍历不要用小于 int main() { list int lt;//创建一个模版对象lt //然后进行数据的插入操作 lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_back(4);//列表 list int::iterator it lt.begin(); while (it ! lt.end()) {cout *it ;it ; }return 0; }迭代器是容器通用的遍历方式 我们这里不能写小于因为前后直接可能不存在大小关系所以我们这里使用≠ 2.4 auto自动推导数据类型 如果想推导出引用的类型的话我们就得在auto的右边加一个 而且如果我们需要进行推导的话那么我们的右边就必须要有一个值不然是不能进行推导操作的因为我们不知道类型是啥推导不出来就会报错 auto真正的价值到底是什么呢像下面的迭代器很长的话我们就可以使用auto了 如果一个类型很长的话我们要写对应的迭代器的话那么就太长了 auto的核心价值就是为了方便 简化代码替换长类型写起来长的类型 auto是一个语法糖 2.5 范围for C遍历容器的一个东西 auto和范围for都是C11提供的 上面我们有下标[]和迭代器进行string进行遍历 那么我们现在可以使用范围for进行遍历的操作 范围for就是一个智能遍历我们之前都是带有逻辑进行一个遍历的操作比如说指针里面的像指针一样的操作 范围for不存在逻辑 我们这里自动取容器对象的数据赋值给左边的值随便定义 自动自动判断结束 我们依次取s1中的数据赋值给ch,然后进行打印操作 下面的链表也是一样的 //范围for我们给的字符串for (char ch : s1){cout ch ;}cout endl;//范围for 遍历上面的链表for (char e : lt){cout e ;}cout endl; 但是真正的写法不这么写的,我们利用auto自动将数据进行推出来 利用auto取出什么类型的数据就放到变量里面就行了 //范围for我们给的字符串for (auto ch : s1){cout ch ;}cout endl;//范围for 遍历上面的链表for (auto e : lt){cout e ;}cout endl; 那么这里就是范围for和auto的一个融合操作 我们在中间进行一个修改的操作但是最后的结果并没有什么变化 为什么没有起作用呢 那么这里进行一个说明 这个范围for的作用是将自动容器的数据赋值给左边的对象 然后就是相当于拷贝的操作 那么我们在循环里面对拷贝的数据进行修改但是不影响原来容器中存在的数据的 如果我们想通过这个拷贝的对容器中的数据进行一个修改的操作 那么我们就需要在auto的后面加上一个 我们可以通过引用进行原来数据的改变的操作的 范围for的话存在两种情况我们是需要进行引用的操作的 一种是我们需要对容器中的数据进行一个修改操作的 一种是容器中存放的是比较大的对象那么我们使用引用可以少一次拷贝的操作 如果我们现在引用了一个很大的数但是不想这个数后面被修改那么我们就在auto的前面加一个const进行限制 那么现在进行回顾下对于string而言的话我们遍历数组有三种方法 下表[] 、 迭代器 、范围for 范围for是用来遍历容器的就是这些数据结构的 原因是范围for的底层是迭代器 下面的两个代码编译成指令以后在底层几乎是类似的都变成了去调用迭代器了 范围for和suto都是语法糖都是为了简化代码用起来更加方便 从此以后遍历数组会更加方便了 范围for适用于容器和数组的遍历操作 auto当前是不能作为形参的 C20开始支持auto作参数 auto可以支持作为返回值的 auto写返回值得把注释写清楚了 #define _CRT_SECURE_NO_WARNINGS 1 #includeiostream #includestring #includeassert.h #includelist using namespace std;//class string //{ //public: // char operator[](size_t pos) // { // assert(pos _size); // } //private: // char* _str; // size_t _size; // size_t _capacity; //}; //int main() {////我们现在想进行字符串的遍历那么我们就可以使用string类里面的operator[]//operator[]可以访问数组的第pos位置的元素越界就会直接报错的string s1(hello world);//通过下标[]进行数组元素的遍历操作for (size_t i 0; i s1.size(); i){cout s1[i];//直接访问第i个位置的字符}string::iterator it1 s1.begin();//用这个类型定义一个对象//我们这里的话begin访问的是h这个字符end指向的是\0//这里将begin给到了it1while (it1 ! s1.end())//end是最后一个数据的下一个位置{//下面的用法非常像指针但是不是指针cout *it1 ;it1;}list int lt;//创建一个模版对象lt//然后进行数据的插入操作lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);//列表//list int::iterator it lt.begin();auto itlt.begin();while (it ! lt.end()){cout *it ;it ;}//范围for我们给的字符串for (auto ch : s1){cout ch ;}cout endl;//范围for我们给的字符串for (auto ch : s1){ch;}cout endl;for (auto ch : s1){cout ch ;}cout endl;//范围for 遍历上面的链表for (auto e : lt){cout e ;}cout endl;return 0; } //这个s1是一个类对象然后我们利用这个string类里面的运算符// //int main() //{ // list int a1; // return 0; //}2.6 迭代器第二层 我们现在如果想倒着遍历呢 下标肯定是可以进行倒着遍历的那么迭代器怎么进行实现这个操作呢 迭代器分为Iterator(正向迭代器) 还有一个叫做反向迭代器reverse iterator int main() {string s1(hello world);string::reverse_iterator rit s1.rbegin();//这个rbegin我们可以想成指向最后一个位置while (rit!s1.rend())//rend指向第一个数据的前一个位置{cout *rit ;rit;//往左边走}cout endl;//上面的代码就实现了逆序输出的操作了return 0; }#define _CRT_SECURE_NO_WARNINGS 1 #include iostream #includestring using namespace std;int main() {string s1 hello world;const string s2(s1);string::iterator it1 s2.begin();while (it1 ! s2.end()){cout *it1 ;}cout endl;return 0; }const对象调用的begin是第二个const对象不能调用普通迭代器所以上面的代码我们是运行不了的 正确的修改方式是代码进行匹配 int main() {string s1 hello world;const string s2(s1);string::const_iterator it1 s2.begin();while (it1 ! s2.end()){cout *it1 ;}cout endl;return 0; }我们在iterator前面加上const_就可以解决这个问题了 因为我们的s2是拷贝s1的const对象那么我们后面的begin(也是调用的是const的版本的 那么我们的这个对象就不能进行修改操作了比如下面的操作就会报错 int main() {//正序string s1 hello world;const string s2(s1);string::const_iterator it1 s2.begin();while (it1 ! s2.end()){cout *it1 ;}cout endl;//逆序string::const_reverse_iterator rit1 s2.rbegin();while (rit1! s2.rend()){cout *rit1 ;rit1;}cout endl;return 0; }不管是正序遍历还是逆序遍历对于这个const对象而言我们都需要在iterator前面加上const_ 下面是改进的地方我们直接使用auto自己推出数据的类型 int main() {//正序string s1 hello world;const string s2(s1);//string::const_iterator it1 s2.begin();auto it1 s2.begin();while (it1 ! s2.end()){cout *it1 ;}cout endl;//逆序//string::const_reverse_iterator rit1 s2.rbegin();auto rit1 s2.rbegin();while (rit1! s2.rend()){cout *rit1 ;rit1;}cout endl;return 0; }2.7 size和length获取字符串的长度 int main() {string s1(hello world);cout s1.size() endl;cout s1.length() endl;//最后我们得到的就是11//是不包含最后结尾的\0的return 0; }计算长度是不包含最后结尾的\0的 2.8 max_size 获取这个字符串能设置的最大长度 不同编译器返回的结果是不同的 int main() {string s1(hello world);cout s1.max_size() endl;//9223372036854775807//不同编译器返回的结果是不同的return 0; }2.9 capacity int main() {string s1(hello world);cout s1.capacity() endl;//15return 0; }计算存储的空间大小其实我们这里11个字符但是我们是15个空间空间实际是16个 capacity是指的是能存储多少个有效字符 扩容要一次多开点空间 2.10 clear 直接将空间清0但是空间大小不变 2.11 operator以及at at越界的话是捕获异常的但是[]越界之后是通过断言进行处理的 对于[]而言的话程序直接终止而且会有一个弹窗的 at是可以抛异常我们是可以进行捕获操作的 2.12 push_back尾插一个字符 int main() {string s1(hello world);s1.push_back( );s1.push_back(x);cout s1 endl;return 0; }2.13 append 尾插一个字符串 int main() {string s1(hello world);s1.push_back( );s1.push_back(x);cout s1 endl;s1.append(abc);cout s1 endl;return 0; }利用append进行多个字符的追加 int main() {string s1(hello world);s1.push_back( );s1.push_back(x);cout s1 endl;s1.append(abc);cout s1 endl;s1.append(10, !);cout s1 endl;//hello world xabc!!!!!!!!!!return 0; }我们在对应的string对象后面加上一段迭代区间 int main() {string s1(hello world);s1.push_back( );s1.push_back(x);cout s1 endl;s1.append(abc);cout s1 endl;s1.append(10, !);cout s1 endl;//hello world xabc!!!!!!!!!!string s2(hello bit hello world);s1.append(s2.begin(), s2.end());cout s1 endl;return 0; }但是现在我想从这个bit开始进行追加的操作 我们在begin()后面进行6的操作 2.14 operator 其实上面的append和push_back我们除了特定的方法我们是不会使用到的 我们使用就行了 int main() {string s1(hello world);string s2(hello bit hello world);string s3(hello);s3 ,;s3 world;cout s3 endl;return 0; }2.15 reserve 提前开空间避免扩容的操作 这个需要和之前的reverse进行区分 reserve是保留、预留的意思 利用reserve开出一个比我们给的空间更大的空间 int main() {string s1;s1.reserve(200);//给出一个比200大的空间这里就是207size_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.16 题目1仅仅反转字母 https://leetcode.cn/problems/reverse-only-letters/ class Solution { public:bool isLetter(char ch){if(chachz){return true;}if(chAchZ){return true;}return false;}string reverseOnlyLetters(string S){if(S.empty())//检查字符串是空的话我们直接将s进行返回了{return S;}size_t begin0,endS.size()-1;//创建两个指针while(beginend){while(beginend!isLetter(S[begin]))//不是字母的话就直接跳过了不进行后面的交换操作了{begin;}while(beginend!isLetter(S[end])){--end;}swap(S[begin],S[end]);begin;--end;}return S;//返回结果} };2.17 题目二字符串相加 https://leetcode.cn/problems/add-strings/description/ class Solution { public:string addStrings(string num1, string num2){string str;int end1num1.size()-1,end2num2.size()-1;//指向结束位置int next0;//进位while(end10||end20)//两个都结束才能结束有一个没结束就可以继续{//分别取到对应的值//如果有一个结束的话我们就给个0就行了int x1end10?num1[end1--]-0:0;//字符0的ASCII是48,1是49转换成对应的整型值int x2end20?num2[end2--]-0:0;//计算完之后我们进行--操作int retx1x2next;nextret/10;retret%10;/*假如我们的x11 x22 那么ret30next3/100那么我们就不会进位ret3%103如果相加的是小于10的值的话是不会有影响的如果我们是9918的话那么nextnext18/101ret18%108那么我们就需要进位1了小于10的模10不会变大于10的模10就将个位留了下来*///我们将结果到开始设置的string 字符串//这里不能用尾插得头插//str(0ret);//几加0就变成字符几了//头插str.insert(str.begin(),0ret);//第一个位置就是迭代器}if(next1)//这个进位还没有进行处理的操作如果进位是0的话还没有关系的{str.insert(str.begin(),1);//插入一个字符1}return str;} };头插的话是会拉低效率的 头插一个数据那么原先的数据就得往后挪动 那么我们如何优化这个程序呢 我们就使用尾插的操作,然后使用reverse进行逆置操作 但是我们的尾插需要进行申请空间的操作那么我们先使用reserve将空间开好了然后就可以避免扩容操作了 str.reserve(max(num1.size(),num2.size())1);//避免扩容 class Solution { public:string addStrings(string num1, string num2){string str;str.reserve(max(num1.size(),num2.size())1);//避免扩容int end1num1.size()-1,end2num2.size()-1;//指向结束位置int next0;//进位while(end10||end20)//两个都结束才能结束有一个没结束就可以继续{//分别取到对应的值//如果有一个结束的话我们就给个0就行了int x1end10?num1[end1--]-0:0;//字符0的ASCII是48,1是49转换成对应的整型值int x2end20?num2[end2--]-0:0;//计算完之后我们进行--操作int retx1x2next;nextret/10;retret%10;/*假如我们的x11 x22 那么ret30next3/100那么我们就不会进位ret3%103如果相加的是小于10的值的话是不会有影响的如果我们是9918的话那么nextnext18/101ret18%108那么我们就需要进位1了小于10的模10不会变大于10的模10就将个位留了下来*///我们将结果到开始设置的string 字符串str(0ret);}if(next1)str1;//我们写一个逆置函数就行了reverse(str.begin(),str.end());return str;} };这样就可以极大的提高了效率 2.18 resize rresize的功能还是比较复杂的有插入删除扩容的操作 #define _CRT_SECURE_NO_WARNINGS 1 #includeiostream #includestring using namespace std; int main() {string s1(1111111111111111111111);cout s1 endl;cout s1.size() endl;cout s1.capacity() endl;//nsize 删除s1.resize(15);cout s1 endl;cout s1.size() endl;cout s1.capacity() endl;//sizencapacity 插入s1.resize(25, x);cout s1 endl;cout s1.size() endl;cout s1.capacity() endl;//ncapacity 进行扩容s1.resize(40,x);cout s1 endl;cout s1.size() endl;cout s1.capacity() endl;return 0; } //如果我们resize给的值比原先的size的值小的话那么就会将多余的删除了 //如果我们resize给的值比原先的size的值大的话新增的元素会使用默认值进行填充如果我们resize给的值比原先的size的值小的话那么就会将多余的删除了 如果我们resize给的值比原先的size的值大的话新增的元素会使用默认值进行填充 resize在string中使用的不多但是vector中用的多 在C中resize() 通常与STL容器如 std::vector、std::string 等有关。它用于调整容器的大小。 2.18.1 使用 std::vector 的 resize() 函数 resize() 函数可以改变 std::vector 的大小。如果新大小比当前大小大新增的元素会使用默认值进行填充如果新大小比当前大小小后面的元素将被移除。 语法 void resize(size_t new_size); void resize(size_t new_size, const T value);new_size: 目标大小类型为 size_t。 value: 当新大小比当前大小大时用来填充新增元素的值可选参数。 示例 调整为更大的大小 #include iostream #include vectorint main() {std::vectorint vec {1, 2, 3};// 调整大小为5新增元素使用默认值0vec.resize(5);for (int i : vec) {std::cout i ;}return 0; }输出 1 2 3 0 0调整为更小的大小 #include iostream #include vectorint main() {std::vectorint vec {1, 2, 3, 4, 5};// 调整大小为3移除后面的元素vec.resize(3);for (int i : vec) {std::cout i ;}return 0; }输出 1 2 3使用特定值填充新增元素 #include iostream #include vectorint main() {std::vectorint vec {1, 2, 3};// 调整大小为5新增元素使用特定值10vec.resize(5, 10);for (int i : vec) {std::cout i ;}return 0; }输出 1 2 3 10 10总结 resize() 可以灵活地增加或减少容器的大小增加的部分可以使用默认值或者指定的值填充。减少大小则会移除多余的元素。 2.19 insert 在指定位置进行字符串的插入操作操作 int main() {string s1(hello world);s1.insert(5,xxxx);//在第5个位置进行中间插入//后面的数据就是往后面进行挪动的cout s1 endl;//helloxxxx worldreturn 0; } 2.20 erase int main() {string s1(hello world);s1.insert(5,xxxx);//在第5个位置进行中间插入//后面的数据就是往后面进行挪动的cout s1 endl;//helloxxxx worlds1.erase(5, 4);//将插入的给删除了cout s1 endl;return 0; 将我们上面利用insert插入的元素删除了 所以erase得作用是从指定位置开始进行元素的删除操作 如何通过ereasr实现头删呢 第0个位置进行头删操作第0个位置开始删除一个元素 还有一种就是我们给个迭代器begin().直接从字符串开始进行删除操作 如果我们只给一个参数的话就从我们给的位置开始进行删除操作后面的元素都删除了这是因为我们没有指定删除的元素的个数 2.21 repalce 替换e 如果我们对两个元素进行替换的话是没有问题的但是如果我们将一个字符替换成多个字符的话那么就意味着挪动数据了 replace在对平替的效率比较高当前是几个字符就替换几个字符 利用find找到空格返回空格的位置然后我们就行这个位置的替换操作 int main() {string s1(hello woprld hello bit);s1.replace(5, 1, %%);//从第5个字符开始的第一个字符替换成%%//本质是将后面的字符往后挪了一位的但是效率很低的cout s1 endl;size_t i s1.find( );//没有找到就返回npos 返回了就正常返回下标while (i ! string::npos){s1.replace(i, 1, %%);//找到空格然后从这个位置开始的一个空格替换为%%i s1.find( );//找到了就一直进行替换}cout s1 endl;return 0; }find的默认位置是从0开始找的我们给什么值就从什么位置开始找 下面是优化的 int main() {string s1(hello woprld hello bit);s1.replace(5, 1, %%);//从第5个字符开始的第一个字符替换成%%//本质是将后面的字符往后挪了一位的但是效率很低的cout s1 endl;size_t i s1.find( );//没有找到就返回npos 返回了就正常返回下标while (i ! string::npos){s1.replace(i, 1, %%);//找到空格然后从这个位置开始的一个空格替换为%%i s1.find( ,i2);//第一次们已经找到了这个空格然后我们就从这个i2的位置开始找空格就行了}cout s1 endl;return 0; }每次替换都需要进行数据的挪动这种效率很低的 还有一种写法 利用范围for int main() {string s1(hello woprld hello bit);cout s1 endl;string s2;for (auto ch : s1)//遍历s1{if (ch ! ){s2 ch;}else{s2 %%;}}cout s2 endl;s1 s2;return 0; } //相当于造出了一个新的字符,然后进行赋值操作2.22 rfind 倒着找 下面是我们进行实现我们获取文件的后缀的操作 先利用rfind倒着找然后找到的话我们利用substr获取子串从我们的pos位置开始取 int main() {string s3(test.cpp);//现在我们想将后缀取出来//那么我们后缀的特征就是前面有一个点以点进行区分操作//那么这个时候就可以使用rfind了size_t pos s3.rfind(.);if (pos ! string::npos)//找到的话{//这个时候我们就找到位置了那么我们就要利用substr这个从pos位置开始取子串string sub s3.substr(pos);//从pos位置开始取cout sub endl;} }find就是正向找 rfind就是倒着找 2.23 find_first_of int main() {std::string str(Please, replace the vowels in this sentence by asterisks.);cout str endl;std::size_t found str.find_first_of(aeiou);while (found ! std::string::npos){str[found] *;found str.find_first_of(aeiou, found 1);}std::cout str \n;return 0; }只要你这个字符串里面有我给的这个字符串里面任意一个字符的话我们就将这个字符变成* 将元音字母变成* 2.24 find_last_not_off int main() {std::string str(Please, replace the vowels in this sentence by asterisks.);cout str endl;std::size_t found str.find_first_not_of(aeiou);while (found ! std::string::npos){str[found] *;found str.find_first_not_of(aeiou, found 1);}std::cout str \n;return 0; }保留元音字母其他字母全部屏蔽掉只有aeiou留下来了 2.25 substr 从pos位置取len个字符 然后将这几个衣服构成资格子串string 2.26 to_string 将数据转换为字符串 2.27总结 常用的构造、迭代器、容量里面的几个 3. string类的模拟实现 3.1 string的模拟实现 string.h #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #includeiostream #includeassert.husing namespace std;namespace kaizi {class string{public://typedef char* iterator;using iteratorchar*;//迭代器using const_iterator const char*;//迭代器//无参构造函数//string();//带参构造函数//如果用strlen进行计算的话都是运行的时候计算o(n)复杂度//这里三个strlen就很坑了//那么我们第一个将size进行初始化就行了后面的初始化直接用size进行初始化操作就行了//但是还是存在风险的我们让一个走初始化就行了剩下的走函数体就行了string(const char* str );//缺省参数只能在声明给//无参的构造函数和右参数的构造函数尽量合并下提供一个全缺省的string(const string s);//string operator(const string s);//赋值string operator(string s);//析构函数~string();//遍历// void reserve(size_t n);void push_back(char ch);void append(const char* str);string operator(char ch);string operator(const char* str);void insert(size_t pos, char ch);void insert(size_t pos, const char *str);void erase(size_t pos, size_t lennpos);size_t find(char ch, size_t pos 0);//查找字符size_t find(const char* str, size_t pos 0);//查找字符串void clear()//将内容都清理掉{_str[0] \0;//直接将第一个元素变成\0了_size 0;}//普通对象版本char operator[](size_t i){assert(i _size);return _str[i];}//const版本const char operator[](size_t i) const{assert(i _size);return _str[i];}iterator begin(){//返回开始位置的迭代器return _str;//返回第一个位置的指针}iterator end(){//返回结束位置的迭代器return _str_size;//返回最后一个位置的下一个位置的指针}const_iterator begin()const{//返回开始位置的迭代器return _str;//返回第一个位置的指针}const_iterator end()const{//返回结束位置的迭代器return _str _size;//返回最后一个位置的下一个位置的指针}size_t size() const{ return _size;}const char* c_str() const{return _str;}void swap(string s);string substr(size_t pos, size_t len npos);private:size_t _size;size_t _capacity;char* _str;public://静态成员变量是不能给缺省值的但是const静态可以static const size_t npos-1;//静态成员变量是不能给缺省值的类里面声明类外面定义//静态成员变量在.h文件声明在.cpp文件定义/*声明 静态成员变量在头文件.h 文件中是为了让类的使用者知道该类有这个静态成员变量。 定义 静态成员变量在实现文件.cpp 文件中 是为了分配内存并避免多次定义的问题。*/};void swap(string s1, string s2);//全局函数bool operator(const string lhs, const string rhs);bool operator!(const string lhs, const string rhs);bool operator(const string lhs, const string rhs);bool operator(const string lhs, const string rhs);bool operator(const string lhs, const string rhs);bool operator(const string lhs, const string rhs);//istream和ostream都要用引用如果是用传值肯定是不行的不支持拷贝的ostream operator(ostream os, const string str);istream operator(istream is, string str);//我们要进行输出那么我们就不能带constistream getline(istream is, string str,char delim\n);//我们要进行输出那么我们就不能带const} string.cpp #define _CRT_SECURE_NO_WARNINGS 1 #includestring.h namespace kaizi//多个命名空间会认为是一个的 {//const size_t string::npos-1;//string::string()// :_str(new char[1] {\0})//不能是nullptr这个调用string类里面的重载函数会报错的// , _size(0)// , _capacity(0)//{//}//带参构造函数//如果用strlen进行计算的话都是运行的时候计算o(n)复杂度//这里三个strlen就很坑了//那么我们第一个将size进行初始化就行了后面的初始化直接用size进行初始化操作就行了//但是还是存在风险的我们让一个走初始化就行了剩下的走函数体就行了string::string(const char* str):_size(strlen(str)){_capacity _size;_str new char[_size 1];//1d的原因是strlen不计算\0的大小我们还得多加一个空间给\0预留strcpy(_str, str);//将源字符串 str 复制到目标字符串 _str 中。}//s2(s1)//传统写法//string::string(const string s)//{// //拷贝构造不能让他们指向同一块空间解决方法就是深拷贝// //不是单纯的对值进行拷贝而是对资源同样也是需要进行拷贝的// //我们再开一段空间有着一样的值// _str new char[s._capacity 1];// strcpy(_str, s._str);// _size s._size;// _capacity s._capacity;//}// //s2(s1)//现代写法string::string(const string s)//这里你的this-_str是野指针没有初始化哦 :_str(nullptr){string tmp(s._str);//构造一个tmp的对象//开一段一样大的空间然后将数据进行拷贝//tmp和s1有着一样大的空间一样大的值//并且tmp里面有着和s1一样的数据//然后我们将s2和tmp进行交换//这个就相当于s2是s1的拷贝swap(tmp);//这里本质是this调用的swap函数//tmp是局部对象出了作用域就会被销毁//这里的swap调用的是成员函数的swap//拷贝构造可以是这个思路那么赋值也可以是这个思路}//s1s3//不能自己给自己赋值如果自己给自己赋值的话我们释放的时候就会出问题了//string string::operator(const string s)//赋值//{// if (this ! s)// {// //这里的this指针指向我们的s1// delete[] _str;//先将我们的空间释放了然后再开一块一样大的空间// _str new char[s._capacity 1];// strcpy(_str, s._str);// _size s._size;// _capacity s._capacity;// }// return *this;//}//赋值的现代写法//string string::operator(const string s)//赋值//{// if (this ! s)// {// string tmp(s._str);//构造一个tmp的对象// swap(tmp);//这里本质是this调用的swap函数// }// return *this;//}//s1s3//我们这里传值传参然后将s3拷贝给s了//然后我们在函数里面利用隐藏的this调用成员函数交换将s的值交换给s1//那么我们就实现了s3赋值给s1的操作了string string::operator(string s)//这里调用的是传值传参传值传参就要调用拷贝构造{//这里存在一个this指针swap(s);//这里本质是this调用的swap函数return *this;//s1传值调用拷贝构造s3去拷贝构造这个s//s就开了一段和s3一样大的空间一样的值//我们让s和s1进行一个交换//那么s1就指向s拷贝构造出的东西了//那么s1指向的空间和s3指向的空间一样大了}//现代写法没有效率的提升只是简介一点本质是复用//析构函数string::~string(){delete[] _str;//C 中用于释放动态分配的数组内存的语句_str nullptr;_size 0;_capacity 0;}void string::reserve(size_t n)//进行扩容操作{if (n _capacity){char* tmp new char[n 1];//开空间永远要多开一个位置因为\0是不计算_capaicty的strcpy(tmp, _str);//将_str拷贝到新空间里面delete[]_str;_str tmp;_capacity n;}}void string::push_back(char ch){尾插之前先看内存够不够再进行尾插的操作//if (_size _capacity)//满了//{// reserve(_capacity 0 ? 4 : _capacity * 2);//如果大小是0的话我们给个4如果不是的话我们进行两倍扩容操作//}//_str[_size] ch;//在size位置插入字符然后_size就行了//_size;//当我们实现insert之后我们直接写insert就行了insert(_size, ch);}void string::append(const char* str){//size_t len strlen(str);//要插入的字符个数//if (_size len _capacity)//{// size_t newcapacity 2*_capacity;// //扩容二倍不够则需要多少扩多少// if (newcapacity _size len)// {// newcapacity _size len;// }// reserve(newcapacity);//} 进行插入操作//strcpy(_str _size, str);//将str拷贝到_str_size这个位置_str是字符串的字符指针//_size len;//已经插入了那么我们的_size就需要更新了 加上插入的字符串的长度insert(_size, str);}string string::operator(char ch){push_back(ch);return *this;}string string::operator(const char* str){append(str);return *this;}void string::insert(size_t pos, char ch)//在pos位置插入字符{//先判断内容够不够if (_size _capacity)//满了{reserve(_capacity 0 ? 4 : _capacity * 2);//如果大小是0的话我们给个4如果不是的话我们进行两倍扩容操作}//将Pos位置后面的数据往后挪动//使用 int 进行循环和索引操作便于处理负数和一些边界情况。尽管 pos 不太可能是负数因为它是 size_t 类型的无符号整数// 但将其转换为 int 可以在处理逻辑时让程序更灵活。//所以这里我们需要将pos从size_t转换为int类型的数据//pos0的时候存在风险的//下面的这个逻辑是进行强转的但是我们假如不进行强转的话//我们将end初始化为_size1//int end _size;//while (end (int)pos)//{// _str[end 1] _str[end];// --end;//}//_str[pos] ch;//进行数据的插入//_size;size_t end _size1;//我们让end的起点位置从_size变成_size1while (end pos)//在pos后面就停下来{_str[end] _str[end-1];//我们将end的前一个位置挪过来--end;}_str[pos] ch;//进行数据的插入_size;}void string::insert(size_t pos, const char* str)//在pos位置插入串{assert(pos _size);//不能越界插入了size_t len strlen(str);//要插入的字符个数if (_size len _capacity){size_t newcapacity 2 * _capacity;//扩容二倍不够则需要多少扩多少if (newcapacity _size len){newcapacity _size len;}reserve(newcapacity);}//int end _size;//while (end (int)pos)//{// _str[end len] str[end];//end位置的元素挪到endlen// end--;//}// // size_t end _sizelen;//pos len - 1 是插入的起点的最后一个字符的位置// 这确保了数据不会在移动过程中覆盖自己。//如果不使用 pos len - 1直接用 pos 可能会导致覆盖还没挪动的数据或者处理越界错误。因此通过这个条件// 可以防止在数据搬移的过程中发生越界和错误写入的情况。while (end poslen-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){if (len _size - pos)//如果给的len是空的话我们直接将后面的全部删除了//len npos 如果len要删除的长度大于pos后面剩余的元素的长度的话那么我们直接将后面全部删除了{_str[pos] \0;_size pos;}else//仅仅删除一部分{//假设删除pos位置后面的两个数的话那么剩下的元素就得往前面挪动了///这个end的位置指向的就是要删除的元素后面的第一个元素开始的位置//将这些元素挪动到要删除元素的位置上进行覆盖操作size_t end pos len;while (end _size)//从end这个位置开始将后面的数据挪动到前面来{_str[end - len] _str[end];//将end的值往前挪进行覆盖操作end;}_size - len;//少了len个元素那么我们直接将size进行减去len的操作}}size_t string::find(char ch, size_t pos )//查找字符{assert(pos _size);//pos不能越界了for (size_t i pos; i _size; i){if (ch _str[i]){return i;}}return npos;//没有找到}size_t string::find(const char* str, size_t pos )//查找字符串{assert(pos _size);//pos不能越界了//返回的是一个指针str2在str1中的位置const char* ptr strstr(_strpos, str);//从pos位置开始找的if (ptr nullptr){return npos;//没有匹配的就返回空}else{return ptr - _str;//返回的指针减去数组开头的指针就是我们要找的字符串开始的下标}//返回的是下标 }string string::substr(size_t pos, size_t len)//获取pos位置后面len个字符串{assert(pos _size);//pos不能越界了//如果大于后面剩余串的长度那么我们直接取到结尾就行了if (len (_size - pos))//size-pos就是我们这个串剩下的字符了{len _size - pos;}kaizi::string sub;sub.reserve(len);//将空间开好for (size_t i 0; i len; i){sub _str[pos i];//从pos位置开始的len个字符全部加等到我们的sub数组中}return sub;//传值返回也是需要拷贝构造的}void string::swap(string s){std::swap(_str, s._str);//去命名空间域里面找std::swap(_size, s._size);//去命名空间域里面找std::swap(_capacity, s._capacity);//去命名空间域里面找}///void swap(string s1, string s2){s1.swap(s2);}bool operator(const string lhs, const string rhs){return strcmp(lhs.c_str(), rhs.c_str()) 0;}bool operator!(const string lhs, const string rhs){return !(lhs rhs);}bool operator(const string lhs, const string rhs){return !(lhs rhs);}bool operator(const string lhs, const string rhs){//第一个值比第二个值小的话那么直接返回小于0的数return strcmp(lhs.c_str(), rhs.c_str())0;}bool operator(const string lhs, const string rhs){return !(lhs rhs);}bool operator(const string lhs, const string rhs){return lhs rhs || lhsrhs;}//os就是coutostream 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();//因此str.clear() 是用来确保每次输入操作时//str 都是干净的、没有之前内容的字符串//str.reserve(1024);//进行扩容的操作//如果我们不扩容的话那么我们这里是需要扩容好几次的int i 0;char buff[256]; // 定义一个大小为 256 的字符数组用作缓冲区char ch; // 定义一个字符变量//用流提取的符号会忽略换行所以我们这里使用get//is ch; // 从输入流读取一个字符ch is.get();while (ch ! ch ! \n) // 碰到空格或者是换行符这个循环就结束{buff[i] ch; // 将字符 ch 存储到 buff 的第 i 个位置并使 i 自增// 我们将输出的内容拼接到字符串中if (i 255)//已经放了255个字符了{buff[256] \0;//给\0留一个位置str buff;//这里会一次进行扩容操作的不会一次一次的进行扩容操作的i 0;//将i重置了}ch is.get(); // 再从输入流读取下一个字符}if (i 0){buff[i] \0;str buff;}return is; // 返回输入流对象}/* 每次读取一个字符后buff[i] ch 会将字符放入缓冲区同时增加索引 i。 当 i 达到 255 时意味着缓冲区几乎满了。此时代码将缓冲区的第 256 个字符设为 \0空字符 使其成为一个合法的 C 字符串。 接着str buff 将缓冲区的内容拼接到字符串 str 中然后重置 i 为 0继续存储后续字符。处理剩余字符在循环结束后可能缓冲区还有未满的部分i 0。此时代码会再次将 buff 中的字符加上 \0 并将剩余的内容追加到 str 中。即用即销毁 用几个空间开几个空间 */istream getline(istream is, string str, char delim)//我们要进行输出那么我们就不能带const{//getline是以delim为结束符的//栈上开空间比堆上开空间的效率高str.clear();//因此str.clear() 是用来确保每次输入操作时//str 都是干净的、没有之前内容的字符串//str.reserve(1024);//进行扩容的操作//如果我们不扩容的话那么我们这里是需要扩容好几次的int i 0;char buff[256]; // 定义一个大小为 256 的字符数组用作缓冲区char ch; // 定义一个字符变量//用流提取的符号会忽略换行所以我们这里使用get//is ch; // 从输入流读取一个字符ch is.get();while (ch ! delim) //分隔符{buff[i] ch; // 将字符 ch 存储到 buff 的第 i 个位置并使 i 自增// 我们将输出的内容拼接到字符串中if (i 255)//已经放了255个字符了{buff[256] \0;//给\0留一个位置str buff;//这里会一次进行扩容操作的不会一次一次的进行扩容操作的i 0;//将i重置了}ch is.get(); // 再从输入流读取下一个字符}if (i 0){buff[i] \0;str buff;}return is; // 返回输入流对象}//对于getline只有我们输入固定的结束符 我们才能结束这个操作} test.cpp #define _CRT_SECURE_NO_WARNINGS 1 #includestring.h #includestring #includeiostream using namespace std;void test1() {kaizi::string s1(hello world);std::cout s1.c_str();cout endl;s1[0] x;std::cout s1.c_str();cout endl;for (size_t i 0; i s1.size(); i){cout s1[i] ;}cout endl;kaizi::string::iterator it1 s1.begin();while (it1 ! s1.end()){(*it1)--;it1;}cout endl;it1 s1.begin();while (it1 ! s1.end()){cout *it1 ;it1;}cout endl;//修改// 范围for的底层是迭代器支持的//意味着支持迭代器就是支持范围forfor (auto ch : s1){ch;}for (auto ch : s1){cout ch ;}cout endl;const kaizi::string s3(xxxxxxx);//我们如果想使用范围for的话我们得有const迭代器for (auto ch : s3){//ch;我们这里是不能进行修改操作的cout ch ;}cout endl;s1 hello bit;std::cout s1.c_str();cout endl;s1.insert(6, x);std::cout s1.c_str();cout endl;s1.insert(0, x);//当我们实现头插的时候我们这里就出现问题了//我们是需要考虑下边界问题的std::cout s1.c_str();cout endl;//中间插入string s2(hello bit);s2.insert(6, xxx);std::cout s2.c_str();cout endl;//头插s2.insert(0, xxx);std::cout s2.c_str();cout endl;s2.erase(5);std::cout s2.c_str();cout endl;s2 hah;std::cout s2.c_str();cout endl;cout s2.find(hah) endl;kaizi::string s5 https://bbs.huaweicloud.com/blogs/330588;size_t pos1 s5.find(:);size_t pos2 s5.find(/, pos1 3);//从pos13这个位置开始找//找到的/就是中间的///if (pos1 ! string::npos pos2 ! string::npos)//{// kaizi::string domain s5.substr(pos1 3, pos2 - (pos1 3));// cout domain.c_str() endl;//}//kaizi::string s7(xxxxxxxxxxxxxxxxxxxxxx);//s1 s7;//std::cout s1.c_str();//cout endl;//cout (s3 s3) endl; } void test2() {kaizi::string s1(hello world);kaizi::string s2(xxxxxxxxxxxxxxxxxx);swap(s1, s2);s1.swap( s2);//调用成员函数的swap,既能完成交换代价也很小的cout s1 endl;cout s2 endl;} void test3() {kaizi::string s1(hello world);kaizi::string s2(s1);cout s1 endl;cout s2 endl; } void test4() {kaizi::string s1(hello world);kaizi::string s2(s1);cout s1 endl;cout s2 endl;kaizi::string s3(xxxxxxxxxxxxxxxxxxx);s1 s3;cout s1 endl; }int main() {test4();return 0; } 3.2 三个swap函数 无论我们是调用成员函数还是全局函数都不会调用到这个算法库的swap函数 算法库这个swap这个要深拷贝代价很大 std::string::swap (string str) 这是 std::string 类的成员函数用于交换两个 std::string 对象的内容。 用法str1.swap(str2)这种情况下str1 和 str2 的内容会被交换。 特点这是针对 std::string 的专用函数只能交换两个 std::string 对象。 std::swap (string x, string y) 这是标准库中的一个普通函数它不是 std::string 的成员而是一个全局函数用于交换两个字符串。 用法std::swap(str1, str2)可以直接在两个 std::string 对象之间交换它们的内容。 特点这是一个全局函数功能上与 string::swap 类似交换两个 std::string 的内容。 std::swap (template) 这是 C 标准库中的模板函数 swap用于交换任意类型的两个对象。 它使用模板参数因此可以用于交换任意类型的对象而不仅仅是 std::string。例如你可以交换 int、double、vector 等任何支持赋值的类型。 用法std::swap(a, b)a 和 b 可以是任意类型的对象只要它们支持交换操作。 特点这是一个通用模板广泛适用于所有类型不仅限于 std::string。 3.21 区别 第一个 swap 是 std::string 类的成员函数专门用于交换 std::string 的内容。 第二个 swap 是全局的 std::swap 函数适用于 std::string 对象的内容交换它提供了与 string::swap 类似的功能但不是类的成员函数。 第三个 swap 是一个模板函数它的用途更广泛可以用于交换任意类型的两个对象而不局限于 std::string。 总结起来 如果只涉及 std::string 对象可以使用 std::string::swap 或 std::swap两者效果相同。 如果需要交换其他类型的对象比如 int、float 等应使用模板版本的 std::swap。
http://www.dnsts.com.cn/news/40994.html

相关文章:

  • 商业网站 技术大连网站建设公司排名
  • 建立官方网站win7做网站
  • 网页安全站点设置网站做cdn服务流量
  • 网页设计网站名字零食网站建设的文案策划书
  • 什么网站做问卷好上海昆山网站公司
  • 网站推广推广网站举报平台建设情况
  • 如何进行电子商务网站建设网站建设在会计里算什么资产
  • 网站建设取得实效用windows建设网站好吗
  • 六安网站建设费用网站上线流程
  • 中国风网站配色方案创建一个网站的英文
  • 网站域名更换是怎么做的wordpress博客下载器
  • 用dw制作网站模板下载地址做网站怎么入账
  • 厦门城乡住房建设厅网站首页西安志成网站建设公司
  • 公司网站域名价格西峡微网站建设
  • 无锡网站建设mkdns凡科网站模板下载
  • 重庆哪里可以做网站的旅游景点网站模板
  • 接视频做的网网站企业展示网站 数据库设计
  • 网站怎么做才有收录一对一视频网站开发
  • wordpress主题tstyle怎么优化
  • 门户网站系统建设清单如何设置网站会员
  • 设计一个网站需要什么网站设计报价方案
  • 顺德网站制作案例平台WordPress设置评论通过
  • 备案网站服务内容京东网上商城手机
  • 建设手机版网站需要主机多少空间做网站的服务器有什么作用
  • 网站页面关键字在哪里网页设计网站设计哪个好
  • 个人网站 logo 版权 备案 没用企业门户网站服务器
  • 建设网站都需要哪些资料衡阳网站制作
  • 简单展示网站模板站群cms
  • 网站建设鞍山网业制作与网站建设
  • 阿里云搭建网站多少钱阿里国际站韩语网站怎么做