【C++】STL--String

这一节主要总结string类的常见接口,以及完成了string类的模拟实现。

目录

标准库的String类 

string类常见接口

string类对象的常见构造

string析构函数:~string

string类对象的容量操作

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

string类对象的修改操作

string类非成员函数

string类的模拟实现

经典的string类问题

浅拷贝

深拷贝

传统版写法的String类

现代版写法的String类

string类的模拟实现


标准库的String类 

为了符合C++面向对象的特性,引入了string类,string是表示字符串的字符串类。下面说明一下string类的特点:

1.string是表示字符串的字符串类

2.string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数

3.string类独立于所使用的编码来处理字节,如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

提示: 在使用string类时,必须包含#include头文件以及using namespace std;

string类常见接口

string类对象的常见构造

(重点)1.string()

默认构造,构造空的string类对象,即空字符串

(重点)2.string(const string& str)

拷贝构造,使用str拷贝构造string类对象

(重点)3.string(const char* s)

使用C-string来构造string类对象

4.string(const string& str, size_t pos, size_t len=npos)

从str的第pos个位置,取len个字符构造,如果len超过字符串长度,那么只取到字符串末尾,如果len未给出,那么采用默认参数npos(0xFFFFFFFF)

5.string(const char* s, size_t n)

使用C-string的前n个字符来构造string类对象

6.string(size_t n, char c)

string类对象包含n个字符c

void test_string1()
{string s0;string s1("hello world");string s2(s1);string s3(s1, 5, 3);string s4(s1, 5, 10);string s5(s1, 5);string s6(10, '$');cout << s0 << endl;cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;cout << s5 << endl;cout << s6 << endl;
}

string析构函数:~string

在string类对象生命周期结束时,会被自动调用。

string类对象的容量操作

1.size()和length()

这两个均可以返回字符串有效字符长度,功能一样。可以通过size遍历字符串。有两个功能一样的函数是由于历史原因造成的!

2.max_size() 

返回string最大的大小

3.capacity()

返回string类对象的容量大小。

//查看扩容机制
void test_string4()
{string s;size_t sz = s.capacity();cout << "capacity change:" << sz << endl;cout << "making s growing" << endl;for (int i = 0; i < 100; i++){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed" << sz << endl;}}
}

 

 虽然是15、31...,但是没有包括‘\0’,所以实际大小应该是16、32、48...,第一次是2倍扩容,以后每次是1.5倍扩容(VS平台下)。但是在Linux平台下,扩容机制不太一样:

这就说明,STL是一个标准,具体怎么实现由编译器决定! 

4.clear()

清除对象的数据,但是不会清空间。

 

 5.shrink_to_fit()

在clear()后,为了释放一些已开辟的空间,可以缩容

 

6.reserve()

如果提前知道要开辟空间的大小,可以用reserve()为字符串提前预留空间,防止频繁扩容,因为扩容一般是异地扩容,效率低下。

 避免了频繁扩容!

还有一个问题,reserve会不会缩容呢?不会的!当reserve()比capacity大,才会扩容!

7. resize()

调整字符串大小。

 

 如果采用第一种,那么默认用‘\0’插入,第二种用指定的char插入。

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

 1.operator[],返回pos位置的字符

string s1("hello world");
for (size_t i = 0; i < s1.size(); i++)
{cout << s1[i] << " ";
}

2.迭代器,begin+end,可以通过迭代器的方式遍历字符串,begin获取第一个字符的迭代器,end获取最后一个字符下一个位置的迭代器

string::iterator it1 = s1.begin();while (it1 != s1.end()){cout << *it1 << " ";++it1;}

我们感觉到迭代器很像指针,但是并不是指针!

3.rbegin+rend,支持倒着遍历,循环中用++rit,而不是--rit,对于反向迭代器而言,++就是倒着走

string s1("hello world");
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{cout << *rit << " ";++rit;
}

除了正向和反向的正常iterator外,还有正向和反向的const_iterator,是用来为const string构造迭代器,其特点是只能读不能写

//只读
const string s3("hello world");
string::const_iterator it3 = s3.begin();
while (it3 != s3.end())
{//*it3 += 3;//报错,因为只读cout << *it3 << " ";
}
cout << endl;
//string::const_reverse_iterator

4.用范围for遍历字符串 

for (auto s : s1){cout << s << " ";}

 但是范围for并没有什么特别之处,其底层是迭代器!

string类对象的修改操作

1.pushback()

在字符串末尾尾插字符

2.append()

在字符串后追加字符串

3.operator+=

在字符串后追加字符串str

4.assign()

用新内容覆盖原字符串

5.insert()

在某一个位置插入字符串

注意:insert()尽量不要使用,因为在使用过程中会挪动数据,效率低。

6.erase()

删除字符

同样,erase()函数能不用就不用。 

7.replace()

字符的替换 

8.find()

查找某个字符的位置

小练习:将一个字符串中的空格替换成“%20”。

总结一下,insert、erase、replace能少用就少用,因为基本都要挪动数据,效率不高。

9.c_str

返回C格式字符串

在C++程序中,我们有可能会调用C语言的函数,比如fopen函数,但是在程序中文件名可能是string对象,需要转换成C字符串才行。

void test_string()
{string filename("test.cpp");FILE* fout = fopen(filename.c_str(), "r");
}

10.find

找到某一个字符或字符串第一次出现的位置

例如:获取后缀

void test_string()
{string s1("test.cpp");size_t pos = s1.find('.');if (pos != string::npos){string suffix = s1.substr(pos);cout << suffix << endl;}else{cout << "没有后缀" << endl;}
}

11.rfind

从后往前找某一个字符或字符串第一次出现的位置

例如:获取后缀“file.c.tar.zip”的zip

void test_string()
{string s1("test.c.tar.zip");size_t pos = s1.rfind('.');if (pos != string::npos){string suffix = s1.substr(pos);cout << suffix << endl;}else{cout << "没有后缀" << endl;}
}

string类非成员函数

1.operator+

尽量少用,因为传值返回,导致深拷贝效率低

2.operator>>&&operator<<

输入和输出运算符重载

3.getline

获取一行字符串。弥补了cin>>的缺点,cin遇到了空格或者换行就提取结束。

4.==  != > >= < <=

大小比较

string类的模拟实现

经典的string类问题

首先来看一个经典关于浅拷贝的问题:

class String
{
public:
/*String()
:_str(new char[1])
{*_str = '\0';}
*/
//String(const char* str = "\0") 错误示范
//String(const char* str = nullptr) 错误示范,使用strlen时会涉及解引用
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;
};
// 测试
void TestString()
{String s1("hello bit!!!");String s2(s1);
}

说明:在上面的String类中没有显式定义拷贝构造和赋值运算符重载,因此,当使用s1去构造时s2时,编译器会调用默认构造函数。这样导致的问题是,s1和s2指向同一块内存空间,在释放空间时,同一块空间会被释放两次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

浅拷贝

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

可以用深拷贝解决浅拷贝问题,也就是:每个对象都有一份独立的资源,不要和其他对象共享

深拷贝

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

传统版写法的String类

namespace ghs
{class string{public:string(const char* str=""):_size(strlen(str)){_str = new char[_size + 1];strcpy(_str, str);_capacity = _size;}string(const string& str){_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}//s1 = s2string& operator=(const string& str){char* tmp = new char[str._capacity + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_size = str._size;_capacity = str._capacity;return *this;}~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}private:char* _str = nullptr;size_t _size = 0; size_t _capacity = 0;public:static const int npos;};const int string::npos = -1;
}

在传统版的写法中,拷贝构造和赋值构造都是通过自己手动开空间,然后使用strcpy函数拷贝完成的,下面的现代写法在实现上更为简洁: 

现代版写法的String类

namespace ghs
{class string{public:string(const char* str=""):_size(strlen(str)){_str = new char[_size + 1];strcpy(_str, str);_capacity = _size;}string(const string& s){string tmp(s._str);swap(tmp);}//s1 = s2//string& operator=(const string& str)//{//	string ss(str);//	swap(ss);//	return *this;//}//上面的再优化,这种更推荐string& operator=(string ss)//传值传参,调用拷贝构造{swap(ss);return *this;}~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}private:char* _str = nullptr;size_t _size = 0; size_t _capacity = 0;public:static const int npos;};const int string::npos = -1;
}

在现代写法中,拷贝构造是通过调用string(const char* str="")构造函数来生成临时的string对象(tmp),再将*this和tmp交换,完成拷贝。在赋值构造中,使用传值传参,其实调用拷贝构造,生成临时对象ss,再将ss和*this交换,完成拷贝。

总结:传统版的现代版的效率一样,只是现代版调用了之前已经实现好的函数,所以看起来更加简洁!

string类的模拟实现
 

namespace ghs
{class string{public:/*string():_str(nullptr),_size(0),_capacity(0){}*//*string():_str(new char[1]),_size(0),_capacity(0){_str[0] = '\0';}*/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;}string(const char* str=""):_size(strlen(str)){_str = new char[_size + 1];strcpy(_str, str);_capacity = _size;}//string(const string& str)//{//	_str = new char[str._capacity + 1];//	strcpy(_str, str._str);//	_size = str._size;//	_capacity = str._capacity;//}string(const string& s){string tmp(s._str);swap(tmp);}//s1 = s2//string& operator=(const string& str)//{//	char* tmp = new char[str._capacity + 1];//	strcpy(tmp, str._str);//	delete[] _str;//	_str = tmp;//	_size = str._size;//	_capacity = str._capacity;//	return *this;//}string& operator=(const string& str){string ss(str);swap(ss);return *this;}//上面的再优化,这种更推荐//string& operator=(string ss)//传值传参,调用拷贝构造//{//	swap(ss);//	return *this;//}const char* c_str()const{return _str;}size_t size()const{return _size;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t sz){if (sz > _capacity){char* p = new char[sz + 1];strcpy(p, _str);delete[] _str;_str = p;_capacity = sz;}}void push_back(char ch){//扩容2倍/*if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';*/insert(_size, ch);}void append(const char* str){//扩容//size_t len = strlen(str);//if (_size + len > _capacity)//{//	reserve(_size + len);//}//strcpy(_str + _size, str);///*for (size_t i = 0; i < len; i++)//{//	push_back(str[i]);//}*///_size += len;insert(_size, str);}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}const char& operator[](size_t pos)const{assert(pos < _size);return _str[pos];}size_t capacity()const{return _capacity;}void insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}for (size_t end = _size+1; end > pos; end--){_str[end] = _str[end - 1];}_str[pos] = ch;_size++;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(len + _size);}for (size_t end = _size+len; end > pos+len-1; end--){_str[end] = _str[end-len];}/*for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}*/strncpy(_str + pos, str, len);_size += len;}void erase(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos ||  len>=_size-pos){_size = pos;_str[_size] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}void resize(size_t n, char ch = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_str[n] = '\0';_size = n;}}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t find(char ch, size_t pos = 0)const{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)const{assert(pos < _size);char* tmp = strstr(_str+pos, str);if (tmp != nullptr){return tmp - _str;}else{return npos;}}string substr(size_t pos = 0, size_t len = npos){string s;if (len == npos || len >= _size-pos){for (size_t i = pos; i < _size; i++){s += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){s += _str[i];}}return s;}void clear(){_size = 0;_str[_size] = '\0';}/*bool operator==(const string& str){int ret = strcmp(_str, str._str);return ret == 0;}*/~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}private:char* _str = nullptr;size_t _size = 0; size_t _capacity = 0;public:static const int npos;};const int string::npos = -1;void swap(string& x, string& y){x.swap(y);}bool operator==(const string& s1,const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;}bool operator<(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}ostream& operator<<(ostream& out, const string& str){for (auto ch : str){out << ch; }return out;}istream& operator>>(istream& in, string& str){char ch;str.clear();//in >> ch;ch = in.get();char buff[100];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 99){buff[99] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';str += buff;}return in; }istream& getline(istream& in, string& str){char ch;str.clear();char buff[100];//in >> ch;/*ch = in.get();while (ch != '\n'){str += ch;ch = in.get();}*/ch = in.get();size_t i = 0;while (ch != '\n'){buff[i++] = ch;if (i == 99){buff[99] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';str += buff;}return in;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/743596.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NAS网络存储的简单了解

一、概述 NAS网络存储&#xff0c;即网络附加存储&#xff08;Network Attached Storage&#xff09;&#xff0c;是一种具有很大存储容量的电脑外敷设备&#xff0c;它通过网络直接连接到交换机上。NAS的主要功能是为网络区域存储&#xff08;或磁盘&#xff09;的用户提供数据…

二、TensorFlow结构分析(5)案例

案例&#xff1a; minimize(error) 代码&#xff1a; def linear_regression():# 自实现线性回归# 1&#xff09;准备数据X tf.random.normal(shape[100,1])y_true tf.matmul(X,[[0.8]]) 0.7# 2&#xff09;构造模型# 定义模型参数 用 变量weights tf.Variable(initial_v…

聚观早报 | 比亚迪e2荣耀版上市;华为享界S9正式亮相

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月14日消息 比亚迪e2荣耀版上市 华为享界S9正式亮相 理想汽车L系列改名 极氪全新纯电MPV车型曝光 vivo X100S外…

大数据基础设施搭建 - Doris

文章目录 一、Linux系统要求1.1 设置系统最大打开文件句柄数1.2 设置最大虚拟块的大小1.3 集群中其他安装doris的机器同上调整1.4 重启服务器生效 二、确认需要下载哪个Doris版本三、上传并解压压缩包3.1 创建目录3.2 解压fe3.3 解压be3.4 解压java udf函数3.4.1 解压3.4.2 复制…

金航标kinghelm宋仕强先生说,“金航标,连接世界

金航标kinghelm宋仕强先生说&#xff0c;“金航标&#xff0c;连接世界”。连接器的作用是为两个电路子系统提供一个可分离的界面&#xff0c;一方面使得零部件或子系统的维护或升级不必修改整个系统&#xff1b;另一方面提高了零部件的便携性、外围设备的拓展能力&#xff0c;…

搭建个人智能家居 3 -第一个设备“点灯”

搭建个人智能家居 3 -第一个外设“点灯” 前言ESPHome点灯 HomeAssistant 前言 前面我们已经完成了搭建这个智能家居所需要的环境HomeAssistant和ESPHome&#xff0c;今天我们开始在这个智能家居中添加我们的第一个设备&#xff08;一颗LED灯&#xff09;&#xff0c;如果环境…

Qt 如何搭建Lua的运行环境

一、Lua简介 Lua 是一种强大的、高效的、轻量级的、可嵌入的脚本语言。它支持过程&#xff08;procedural&#xff09;编程、面向对象编程、函数式编程以及数据描述。Lua 是动态类型的&#xff0c;运行速度快&#xff0c;支持自动内存管理&#xff0c;因此被广泛用于配置、脚本…

使用OCC进行旋转扫掠

旋转扫掠是将物体以某一个坐标轴为参照&#xff0c;按照指定的角度旋转生成新的图形的过程 这里使用面的案例&#xff0c;使用线的逻辑处理其实是一样的 //构造旋转轴 gp_Ax1 anAxis; //设置轴的原点 anAxis.SetLocation(0,0,0); //设置轴的方向 anAxis.SetDirection(gp_Dir(0…

Docker与Nacos的下载与安装配置

文章目录 docker作用docker的下载nacos 下载1. 首先搜索需要的下载2. 拉取stars最多的即可3. 启动nacos4. 打开防火墙8848端口5. 访问nacos docker 作用 Docker 是一种开源的容器化平台&#xff0c;它的作用主要包括以下几个方面&#xff1a; 应用程序的打包和分发&#xff1…

【软考高项】五、信息化发展之数字化转型与元宇宙

1、数字化转型 定义&#xff1a;数字化转换、数字化升级基础上&#xff0c;对其业务进行系统性、彻底的&#xff08;或重大和完全的&#xff09;重新定义。 驱动因素&#xff1a; 生产力飞升:第四次科技革命&#xff1a;第一科学范式为经验范式,第二科学范式为理论范式。第三…

Java高级编程—泛型

文章目录 1.为什么要有泛型 (Generic)1.1 泛型的概念1.2 使用泛型后的好处 2.在集合中使用泛型3.自定义泛型结构3.1 自定义泛型类、泛型接口3.2 自定义泛型方法 4.泛型在继承上的体现5.通配符的使用5.1 基本使用5.2 有限制的通配符的使用 1.为什么要有泛型 (Generic) Java中的…

win11 ubuntu子系统 开代理 调试 openai 接口

我的是laravel项目&#xff0c;步骤如下 步骤1&#xff1a;配置WSL以使用代理 首先&#xff0c;确保WSL中的所有请求都通过你的代理服务器。你可以通过在WSL的shell配置文件&#xff08;如~/.bashrc或~/.zshrc&#xff09;中设置环境变量来实现。打开终端&#xff0c;编辑对应…

数据结构知识点总结00-知识点目录

专栏主页&#xff1a; 数据结构算法程序设计基础C语言知识点总结https://blog.csdn.net/seeker1994/category_12585732.html C语言知识点总结00-C语言知识点目录 最优算法100例00-最优算法100例目录 ...... 数据结构知识点目录 要求&#xff1a; &#xff08;1&#xff…

08.JavaScript中的编程思想,构造函数和原型对象

一、编程思想 学习 JavaScript 中基于原型的面向对象编程序的语法实现&#xff0c;理解面向对象编程的特征。 1.面向过程 面向过程就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候再一个一个的依次 调用就可以了。 举个…

小明的背包——01背包问题

经典版 题目链接&#xff1a;1.小明的背包1 - 蓝桥云课 (lanqiao.cn) 01背包问题中&#xff0c;每种物品只有两种状态&#xff0c;即拿或不拿。设状态dp[i][j]max(dp[i-1][j],dp[i-1][j-w]v)&#xff1b;如果不拿物品i&#xff0c;那么最大价值就是dp[i-1][j]&#xff0c;如果…

探索Java高并发编程之道:理论与实践

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 简介 随着互联网和信息技术的快速发展&#x…

【2024 R1 版本更新】Ansys Fluent(上)

​​Ansys2024R1来了&#xff0c;小宇赶紧将新功能给大家汇报一下。GPU求解器功能势头最强&#xff0c;pyFluent又开始迭代了&#xff0c;CPU模型中又更新了很多功能&#xff0c;fluent meshing中的thin volume mesh功能也来了。

libusb_Qt使用

Libusb libusb_github 建议直接下载库&#xff0c;编译好麻烦 QT调用 .pro文件添加&#xff1a; win32: LIBS -L$$PWD/LIB/libusb/x64/ -llibusb-1.0.cpp调用即可 #include "LIB/libusb/libusb.h" void class_name::fun(){/* 1. */libusb_init(NULL);/**/str…

mockjs学习

1.前言 最近面试发现之前团队协同合作的项目没有mock数据难以向面试官直接展示&#xff0c;所以迟到得来速学一下mockjs。 参考视频&#xff1a;mockJs 妈妈再也不用担心我没有后端接口啦_哔哩哔哩_bilibili 一开始查阅了一些资料&#xff0c;先是看了下EasyMock&#xff0c…

环保企业应适应行业发展趋势,不断创新和提升竞争力|中联环保圈

《2023年行业评述及2024年发展展望》一文&#xff0c;由中国环保产业协会撰写&#xff0c;全面审视了过去一年我国生态环保产业的发展状况&#xff0c;并对新的一年发展趋势进行了深度预测。该报告以行业主要政策标准为基础&#xff0c;结合报告以及新冠疫情防控转段后的经济恢…