【C++】string类(学习并实现)(学习复习兼顾)

2024-03-22 08:40

本文主要是介绍【C++】string类(学习并实现)(学习复习兼顾),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

0. 前言

1. 为什么学习string类?

1.1 C语言中的字符串

2. 标准库中的string类

2.1 string类(了解)

总结:

2.2 string类的常用接口说明(最常用的接口)

2.2.1 string类对象的常见构造

2.2.2 string类对象的容量操作

注意:

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

2.2.4 string类对象的修改操作

注意:

2.2.5 string类非成员函数

2.2.6 牛刀小试

3. string类的模拟实现

3.1 经典的string类问题

总结: 

3.2 浅拷贝

3.3 深拷贝

3.3.1 传统版写法的string类

3.3.2 现代版写法的string类

3.3 写时拷贝(理解思路)

4. string类的模拟实现 

string.h(类的实现)

Test.cpp(进行测试)


0. 前言

        此博客为博主以后复习的资料,所以大家放心学习,总结的很全面,每段代码都给大家发了出来,大家如果有疑问可以尝试去调试。

        大家一定要认真看图,图里的文字都是精华,好多的细节都在图中展示、写出来了,所以大家一定要仔细哦~

        感谢大家的喜欢,感谢大家的支持~!


1. 为什么学习string类?

1.1 C语言中的字符串

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


2. 标准库中的string类

2.1 string类(了解)

string类的文档介绍

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类。
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string string。
  4. 不能操作多字节或者变长字符的序列。

2.2 string类的常用接口说明(最常用的接口)

2.2.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) (重点)拷贝构造函数
	string s1; // 构造空的string类对象s1string s2("hello CSDN"); // 用C格式字符串构造string类对象s2string s4 = "hello CSDN";string s3(s2); // 拷贝构造s3

2.2.2 string类对象的容量操作

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

// size/clear/resize
int main()
{// 注意:string类对象支持直接用cin和cout进行输入和输出string s("Hello CSDN!!!");cout << s.size() << endl;cout << s.length() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小s.clear();cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到10个,多出位置用'a'进行填充// "aaaaaaaaaa"s.resize(10, 'a');cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充// "aaaaaaaaaa\0\0\0\0\0"// 注意此时s中有效字符个数已经增加到15个s.resize(15);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中有效字符个数缩小到5个s.resize(5);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;
}

 

 

 

int main()
{string s;// 测试reserve是否会改变string中有效元素个数s.reserve(100);cout << s.size() << endl;cout << s.capacity() << endl;// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小s.reserve(50);cout << s.size() << endl;cout << s.capacity() << endl;return 0;
}

// 利用reserve提高插入数据的效率,避免增容带来的开销void TestPushBack()
{string s;size_t sz = s.capacity();cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}
void TestPushBackReserve()
{string s;s.reserve(100);size_t sz = s.capacity();cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}
}

注意:

  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.2.3 string类对象的访问及遍历操作

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

        首先先要理解类对象的访问:

int main()
{string s1("hello CSDN");const string s2("Hello CSDN");cout << s1 << "   " << s2 << endl;cout << s1[0] << " " << s2[0] << endl;s1[0] = 'H';cout << s1 << endl;// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改return 0;
}

         然后再来理解三种遍历操作:

(整体代码)

int main()
{string s("Hello CSDN");// 3种遍历方式:// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,// 另外以下三种方式对于string而言,第一种使用最多// 1. for+operator[]for (size_t i = 0; i < s.size(); ++i)cout << s[i];// 2.迭代器string::iterator it = s.begin(); // 正向迭代器while (it != s.end()){cout << *it;++it;}cout << endl;string::reverse_iterator rit = s.rbegin(); // 反向迭代器while (rit != s.rend()){cout << *rit;++rit;}cout << endl;string::iterator tp = s.begin(); // "改"试例string::iterator& tmp = tp;while (tmp != s.end()){(*tmp)++;cout << *tmp;++tmp;}// 3.范围forfor (auto ch : s)cout << ch;cout << endl;for (char& e : s){e += 1;}for (auto e : s)cout << e;cout << endl;return 0;
}

(分开分析) 

1. for+operator[]

int main()
{string s("Hello CSDN");// 1. for+operator[]for (size_t i = 0; i < s.size(); ++i)cout << s[i];cout << endl;for (size_t i = 0; i < s.size(); ++i)cout << ++s[i];return 0;
}

2. 迭代器

int main()
{string s("Hello CSDN");// 2.迭代器string::iterator it = s.begin(); // 正向迭代器while (it != s.end()){cout << *it;++it;}cout << endl;string::reverse_iterator rit = s.rbegin(); // 反向迭代器while (rit != s.rend()){cout << *rit;++rit;}return 0;
}

3. 范围for

int main()
{string s("Hello CSDN");// 3.范围forfor (auto ch : s)cout << ch;cout << endl;for (char& e : s){e += 1;}for (auto e : s)cout << e;cout << endl;return 0;
}

2.2.4 string类对象的修改操作

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

(整体代码)

int main()
{string str;str.push_back(' '); // 在str后插入空格str.append("Hello"); // 在str后追加一个字符"Hello"str += 'C'; // 在str后追加一个字符'C'str += "SDN"; // 在str后追加一个字符串"SDN"cout << str << endl;cout << str.c_str() << endl; // 以C语言的方式打印字符串printf("%s\n", str.c_str());// c_str 的意义// printf("%s\n",str); // 类型不匹  乱码// 获取file的后缀string file("string.cpp");size_t pos = file.rfind('.');string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;// npos是string里面的一个静态成员变量// static const size_t npos = -1;// 取出url中的域名string url("http://www.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");if (start == string::npos) // 没有找到{cout << "invalid url" << endl;return 0;}// 从'w'开始 截至到下一个'/'start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;// 删除url的协议前缀size_t pos = url.find("://");url.erase(0, pos + 3);cout << url << endl;return 0;
}

(分开分析) 

int main()
{string str;str.push_back(' '); // 在str后插入空格str.append("Hello"); // 在str后追加一个字符"Hello"str += 'C'; // 在str后追加一个字符'C'str += "SDN"; // 在str后追加一个字符串"SDN"cout << str << endl;cout << str.c_str() << endl; // 以C语言的方式打印字符串printf("%s\n", str.c_str());// c_str 的意义// printf("%s\n",str);return 0;
}

int main()
{// 获取file的后缀string file("string.cpp");size_t pos = file.rfind('.');string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;// npos是string里面的一个静态成员变量// static const size_t npos = -1;return 0;
}

int main()
{// 取出url中的域名string url("http://www.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");if (start == string::npos) // 没有找到{cout << "invalid url" << endl;return 0;}// 从'w'开始 截至到下一个'/'start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;// 删除url的协议前缀size_t pos = url.find("://");url.erase(0, pos + 3);cout << url << endl;return 0;
}

注意:

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

2.2.5 string类非成员函数

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

        上面的几个接口大家了解一下,有些OJ题目中会有一些体现他们的使用。string类中还有一些其他的操作,这里不一一列举,大家在需要用到时不明白了查文档即可。

2.2.6 牛刀小试

1. 917. 仅仅反转字母

class Solution {
public:string reverseOnlyLetters(string s) {size_t left = 0 , right = s.size() - 1;while(left < right){while(left < right && !isalpha(s[left])){left++;}while(left < right && !isalpha(s[right])){right--;}if(left < right){swap(s[left] , s[right]);right--;left++;}}return s;}
};

2. 387. 字符串中的第一个唯一字符

class Solution {
public:int firstUniqChar(string s) {int count[26] = { 0 };for(int i = 0; i < s.size() ; i++){count[s[i] - 'a']++;}for(int i = 0; i < s.size() ; i++){if(count[s[i] - 'a'] == 1)return i;}return -1;}
};

3. 字符串最后一个单词的长度

#include<iostream>
#include<string>
using namespace std;
int main()
{string s;getline(cin, s);size_t pos = s.rfind(' ');cout << s.size() - pos - 1 << endl;return 0;
}

4. 125. 验证回文串

class Solution {
public:bool isLetterOrNumber(char ch){return (ch >= '0' && ch <= '9')|| (ch >= 'a' && ch <= 'z')|| (ch >= 'A' && ch <= 'Z');}bool isPalindrome(string s) {// 先将小写字母转换成大写,再判断for(auto& ch : s){if(ch >= 'a' && ch <= 'z')ch -= 32;}int begin = 0, end = s.size() - 1;while(begin < end){while(begin < end && !isLetterOrNumber(s[begin]))begin++;while(begin < end && !isLetterOrNumber(s[end]))end--;if(s[begin] != s[end])return false;else{begin++;end--;}}return true;}
};

 5. 415. 字符串相加

class Solution {
public:string addStrings(string num1, string num2) {int end1 = num1.size() - 1;int end2 = num2.size() - 1;int value1 = 0, value2 = 0, next = 0;string addret; // 用来接收相加后的值while(end1 >= 0 || end2 >= 0){if(end1 >= 0)value1 = num1[end1--] - '0';elsevalue1 = 0;if(end2 >= 0)value2 = num2[end2--] - '0';elsevalue2 = 0;int valueret = value1 + value2 + nest;if(valueret >= 10){valueret -= 10;next = 1;}elsenext = 0;addret += valueret + '0';}if(next == 1){addret += '1';}reverse(addret.begin(),addret.end());return addret;}
};

3. string类的模拟实现

3.1 经典的string类问题

        上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?

namespace Liyuyue // 命名空间
{class string{public:/*string():_str(new char[1]){*_str = '\0';}*///string(const char* str = "\0") 错误示范//string(const char* str = nullptr) 错误示范string(const char* str = ""){// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下if (nullptr == str){assert(false); // 判断目标元素的内容与预期(是)否一致return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}~string(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;};
}// 测试
int main()
{Liyuyue::string s1("Hello CSDC!!!");Liyuyue::string s2(s1);return 0;
}

总结: 

        上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

3.2 浅拷贝

        浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。要解决浅拷贝问题,C++中引入了深拷贝。

3.3 深拷贝

        如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

3.3.1 传统版写法的string类

class string
{
public:string(const char* str = ""){// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}string(const string& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}string& operator=(const string& s){if (this != &s){char* pStr = new char[strlen(s._str) + 1];strcpy(pStr, s._str);delete[] _str;_str = pStr;}return *this;}~string(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;
};

3.3.2 现代版写法的string类

class string
{
public:string(const char* str = ""){if (nullptr == str)str = "";_str = new char[strlen(str) + 1];strcpy(_str, str);}string(const string& s): _str(nullptr){string strTmp(s._str);swap(_str, strTmp._str);}// 对比下和上面的赋值那个实现比较好?string& operator=(string s){swap(_str, s._str);return *this;}/*string& operator=(const string& s){if(this != &s){string strTmp(s);swap(_str, strTmp._str);}return *this;}*/~string(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;
};

3.3 写时拷贝(理解思路)

         写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

        引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

4. string类的模拟实现 

        string的实现我用了两个文件进行写的,大家可以进行学习,当然也可以使用三个文件进行写,就是比较麻烦(亲测麻烦,才改的两个文件)。

string.h(类的实现)

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>using namespace std;namespace Liyuyue
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}const_iterator begin() const{return _str;}iterator end(){return _str + _size;}const_iterator end() const{return _str + _size;}// 全交换// s1.swap(s2);void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 构造函数string(const char* str = ""){_str = new char[strlen(str) + 1];_size = strlen(str);_capacity = _size;strcpy(_str, str);}// 拷贝构造函数// // 传统写法//string(const string& s)//	:_str(new char[strlen(s._str) + 1])//{//	strcpy(_str, s._str);//}// // 现代写法string(const string& s):_str(nullptr){string tmp(s._str);swap(tmp);}// 赋值运算符重载//	// 传统写法//string& operator=(const string& s)//{//	{//		// 如果是同一个没有意义//		if (this != &s) // s1 = s1;//		{//			char* pstr = new char[strlen(s._str) + 1];//			strcpy(pstr, s._str);//			delete[] _str;//			_str = pstr;//		}//		return *this;//	}//}// // 现代写法//string& operator=(const string& s) //{//	// 如果是同一个没有意义//	if (this != &s)//	{//		string tmp(s);//		swap(tmp);//	}//	return *this;//}string& operator=(string s) // 临时变量,出了作用域就析构{swap(s);return *this;}// 析构函数~string(){if (_str != nullptr){delete[] _str;_str = nullptr;_size = _capacity = 0;}}// 遍历 // at 作用和operator[]类似,越界抛异常// 只读const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}// 可读可写char& operator[](size_t pos){assert(pos < _size);return _str[pos];}// 开空间,扩展capacityvoid reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strncpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}}// 开空间+初始化,扩展capacity 并且初始化空间。size也要动// 有三种情况:// 1. n < size// 2. size < n < capacity// 3. n > capacity;void resize(size_t n, char val = '\0'){if (n < _size){_size = n;_str[_size] = '\0';}else{if (n > _capacity) // 大于就增容{reserve(n);}for (size_t i = _size; i < n; i++) // 遍历改为val{_str[i] = val;}_str[n] = '\0'; // 最后一个一定是'\0'_size = n;}}// 插入字符void push_back(char ch){/*if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_str[_size + 1] = '\0';_size++;*/insert(_size, ch);}// 插入字符串void append(const char* str){/*size_t len = strlen(str) + _size;if (len > _capacity){reserve(len);}strcpy(_str + _size, str);_size = len;*/insert(_size, str);}// s1 += 'x'string& operator+=(char ch){push_back(ch);return *this;}// s1 += "xxxxx"string& operator+=(const char* str){append(str);return *this;}// 任意插入字符string& insert(size_t pos, char ch){assert(pos <= _size); // 这里可以等于,相当于尾插if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}不推荐  这样做就将把pos设为size_t 的意义就没了,//int end = _size;//while (end >= (int)pos)//{//	_str[end + 1] = _str[end];//	end--;//}或者还可以指针//size_t end = _size + 1;//while (end > pos)//{//	_str[end] = _str[end - 1];//	end--;//}char* end = _str + _size;while (end >= _str + pos){*(end + 1) = *end;end--;}_str[pos] = ch;_size++;return *this;}// 任意插入字符串string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str) + _size;if (len > _capacity){reserve(len);}// 挪动数据char* end = _str + _size;while (end >= _str + pos){*(end + len - _size) = *end;end--;}strncpy(_str + pos, str, len - _size);_size = len;return *this;}// 删除string& erase(size_t pos, size_t len = npos){assert(pos < _size);size_t DelLen = _size - pos;// 1、剩余的字符长度小于要删的长度  (后面全部删完)// 2、剩余的字符长度大于要删的长度if (len >= DelLen){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}// 找字符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* ret = strstr(_str + pos, str);// 字符串查找if (ret)return ret - _str;elsereturn npos;}size_t size() const{return _size;}const char* c_str() const{return _str;}void clear(){_size = 0;_str[0] = '\0';}private:char* _str;size_t _size;size_t _capacity; // 我实现的 capacity 不包含\0;static const size_t npos;};const size_t string::npos = -1;inline bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}inline bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}inline bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}inline bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}inline bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}inline bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}// 返回为ostream&(istream&)是因为实现连续读取(写入)ostream& operator<<(ostream& out, const string& s){/*out << s.c_str();return out;*/for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}istream& getline(istream& in, string& s){s.clear();char ch;ch = in.get();while (ch != '\n'){s += ch;ch = in.get();}return in;}void Test1(){string s1("Hello CSDN!!!");string s2(s1);string s3;s3 = s1;s3 += '?';s2 += "wuwu~";s1.reserve(100);s2.resize(3);s2.resize(13);s2[3] = 'l';}void Test2(){string s1("Hello CSDN!!!");cout << s1;s1.insert(6, "Liyuyue ");s1.erase(6, 8);}void test_string1(){string s1("hello world");string s2(s1);string s3("hello CSDN");// C++98,C++11中增加右值引用的移动语义,优化了swap函数模板swap(s1, s3); // 效率低s1.swap(s3); // 效率高,推荐这个}void f(const string& s){// ....for (size_t i = 0; i < s.size(); ++i){//s[i] += 1;cout << s[i] << " ";}cout << endl;}void test_string2(){string s1("hello world");s1[0] = 'x';cout << s1[1] << endl;//cout << s1[100] << endl;cout << s1.c_str() << endl;f(s1);for (size_t i = 0; i < s1.size(); ++i){cout << s1[i] << " ";}cout << endl;}void func(const string& s){string::const_iterator it = s.begin();while (it != s.end()){// *it = 'x';cout << *it << " ";++it;}cout << endl;}void test_string3(){string s1("hello world");string::iterator it = s1.begin();while (it != s1.end()){*it += 1; // 修改cout << *it << " ";++it;}cout << endl;func(s1);// 看起来很神奇,但是原理很简单,这个范围for会被编译器替换成迭代器形式// 也就是说范围for是有迭代器支持的for (auto ch : s1){cout << ch << " ";}cout << endl;}void test_string4(){string s1("hello world");s1 += '#';for (auto ch : s1){cout << ch << " ";}cout << endl;s1 += "xyz";for (auto ch : s1){cout << ch << " ";}cout << endl;s1 += "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1";for (auto ch : s1){cout << ch << " ";}cout << endl;}void test_string5(){string s1("hello");s1 += '!';s1.resize(8, 'x');cout << s1.c_str() << endl;s1.resize(15, 'y');cout << s1.c_str() << endl;s1.resize(3);cout << s1.c_str() << endl;}void test_string6(){string s1("hello");s1.insert(2, 'x');cout << s1.c_str() << endl;s1.insert(0, 'y');cout << s1.c_str() << endl;s1.insert(0, "!!!!");cout << s1.c_str() << endl;s1 += '!';cout << s1.c_str() << endl;s1 += "world";cout << s1.c_str() << endl;s1.erase(2, 5);cout << s1.c_str() << endl;s1.erase(2);cout << s1.c_str() << endl;}void test_string7(){string s1("hello world");cout << s1.find('x') << endl;cout << s1.find('o') << endl;cout << s1.find("wor") << endl;cout << s1.find("worx") << endl;string s2("hello world");s2.resize(20, 'x');s2 += "!!!";cin >> s2 >> s1;cout << s2 << endl;cout << s1 << endl;string line;getline(cin, line);cout << line << endl;}void s(){string s1("Hello CSDN!");s1.resize(3);s1.resize(8);s1[3] = 'l';}
}

Test.cpp(进行测试)

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"int main()
{//Liyuyue::Test1();//s();//Liyuyue::Test2();//Liyuyue::test_string1();Liyuyue::test_string2();Liyuyue::test_string3();Liyuyue::test_string4();Liyuyue::test_string5();Liyuyue::test_string6();Liyuyue::test_string7();return 0;
}

        如上就是 string类 的所有知识,接下来本专辑会更新vector类,让大家轻松使用STL库,理解STL库,并实现STL库,如果大家喜欢看此文章并且有收获,可以时刻关注我,本专栏持续更新!

        感谢大家观看,感谢大家支持!

这篇关于【C++】string类(学习并实现)(学习复习兼顾)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/834960

相关文章

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

IDEA如何将String类型转json格式

《IDEA如何将String类型转json格式》在Java中,字符串字面量中的转义字符会被自动转换,但通过网络获取的字符串可能不会自动转换,为了解决IDEA无法识别JSON字符串的问题,可以在本地对字... 目录问题描述问题原因解决方案总结问题描述最近做项目需要使用Ai生成json,可生成String类型

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

C# 读写ini文件操作实现

《C#读写ini文件操作实现》本文主要介绍了C#读写ini文件操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、INI文件结构二、读取INI文件中的数据在C#应用程序中,常将INI文件作为配置文件,用于存储应用程序的