C++——string

目录

STL

STL六大组件

标准库中的string类

string类

string类常用接口 

构造函数

下标遍历[]

 迭代器

范围for

push_back()

 append()

insert() 

operator+=

pop_back()

erase()

reserve

 resize

 clear

c_str()

substr()

 find()

 rfind()

 find_first_of

getline

string库中的函数

to_string

从字符串装换为其他类型

深浅拷贝

拷贝构造的另一种写法 

赋值运算符重载的另一种写法


在讲解string之前先来简单了解一下STL

STL

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

STL六大组件


标准库中的string类

        为什么说他是标准库中的类呢,因为string比STL出现的还要早;就单单一个string类就有很多的函数接口,那这么多东西都要记住还是很困难的,但是不用担心,我们要记住的是其中常用的一些函数接口,不常用的记不住就去查文档https://cplusplus.com/reference/,个人认为这个文档还是非常好用的。

string类

  1. string是表示动态增长的char类型字符数组的字符串类。
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string; 
  4. 不能操作多字节或者变长字符的序列。
  5. 使用string类时,必须包含#include头文件以及using namespace std;
#include <string>
using namespace std;
string str = "abcdefg";

        在这个basic_string类下还有如图这样的,string使用的是typedef basic_string<char> string; 其他的就不是用char来实例化模板类,比如wstring使用wchar_t,就是宽字符,这样设计自然有它的道理在有些编码的地方需要使用它,主要使用的还是string。

string类常用接口 

构造函数

string ();	
string (const string& str);
string (const char* s);string s1; // 空字符串
string s2 = "abcdefg"; // 构造+拷贝构造->编译器优化为直接构造,这最后当然也包括\0
cout << s1 << endl; // 这里重载了流插入和流提取
string s3(s2); // 拷贝构造,这里肯定要用深拷贝实现string (const string& str, size_t pos, size_t len = npos); // 从sting对象的pos位置截取len长度的字符串
string s4(s2, 1, 2); // 从s2中下标为1的位置开始复制2个字符,上面是这个函数的声明
// 可以看到len有一个缺省参数是npos,len的长度为要几个字符,如果大于剩余的字符就拷贝到结尾
// 如果不传入参数就是npos
// npos是一个静态成员变量,static const size_t npos = -1
// 这是一个无符号的值,所以是一个很大的值,意思就是取到结尾,npos后面用的还是很多的s1 = s2; // 赋值重载

下标遍历[]

// string也支持下标访问
string s1("hello world");
cout << s1[0] << endl;
char& operator[](size_t pos); // 也有const版本,const对象会去匹配
// 引用返回可以减少拷贝,也可以修改返回值size_t size() const; // 返回字符的个数
for (int i = 0; i < s1.size(); i++) // 遍历每一个字符
{}

 迭代器

// 另一种遍历方式,迭代器,现在先理解为一个指针就可以了
string s1("hello");
string::iterator it = s1.begin();
// 在string类下的迭代器,所以其他容器里也有迭代器
// it是一个名字。通常都是it,简单理解就是指针
// iterator begin(); 返回对象的初始位置的指针
// iterator end(); 返回对象的最后一个位置的下一个,所以begin和end的区间是左闭右开
while (it != s1.end()) // 意思就是迭代器一直向后走,直到遇到最后一个位置的下一个
{// *it 解引用拿到这个对象it++;
}// 还有一种就是反向迭代器
string s2("hello");
string::reverse_iterator rit = s2.rbegin(); // rbegin()返回最后一个位置
while (rit != s2.rend()) // rend()返回第一个位置的前一个
{//...rit++;
}// 常量迭代器
string s3("hello");
string::const_iterator cit = s3.cbegin();
while (cit != s3.cend()) 
{//...cit++;
}// 反向常量迭代器
string s4("hello");
string::const_reverse_iterator crit = s4.crbegin();
while (crit != s4.crend())
{//...crit++;
}
        相比于迭代器,string使用下标遍历更好;但是迭代器还是很重要,毕竟有的容器只能用迭代器,它是所有容器通用的访问方式。如果这个声明太长了,也可以使用auto it。如果使用const迭代器的话还是会有const对象和普通对象的权限问题,注意一下就可以。

范围for

string s("hello");
// 范围for -- 自动迭代,自动判断结束
// 依次取s中每个字符,赋值给c
// 它只可以正向遍历
for (auto c : s)
{}// 范围for的底层其实就是迭代器,其实去查一下这两种写法的汇编就知道,基本是一样的
// 还有一个点就是范围for这里是赋值给c的,要是想修改就不行了
// c只是一份拷贝,它的值修改不会影响原来的值
// 所以这里也可以使用引用&
for (auto& c: s)
{}

push_back()

// 在字符串后添加一个字符
void push_back (char c);string s("hello");
s.push_back(' ');

 append()

// 在字符串后追加一个字符串
string& append (const string& str);
string& append (const char* s);
string& append (const char* s, size_t n); // 追加指定的s字符串的前n个字符
string& append (size_t n, char c); // 追加n个字符cstring s("hello");
s.append("_world")

insert() 

 string& insert (size_t pos, const string& str);string& insert (size_t pos, const char* s);	string s("hello");
s.insert(0, "x"); // 在下标为0的位置前插入"x"

operator+=

string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);string s("hello");
s += '_'; // +=一个字符
s += s; // +=一个对象
s += "world"; // +=一个字符串

pop_back()

void pop_back();string s("hello");
s.pop_back(); // 删除字符串的最后一个字符

erase()

string& erase (size_t pos = 0, size_t len = npos); // 不指定删除个数就删到结尾string s("hello")
s.erase(0, 5) // 从下标为0的位置,删除5个字符

reserve

void reserve (size_t n = 0);string s;
s.reserve(1000); // 设置string对象的空间大小为1000字节,最后也不一定是1000字节,会对齐

 resize

void resize (size_t n);
void resize (size_t n, char c);string s;
s.resize(1000, 'x'); // 设置string对象的长度,可以开空间大小为1000字节并且初始化为'x'

 clear

void clear();string s;
s.resize(100, 's');
s.clear(); // 清空string对象

c_str()

const char* c_str() const; // 返回这个字符串的const char*形式string s("hello");
const char* ch = s.c_str();

substr()

string substr (size_t pos = 0, size_t len = npos) const;string s("hello world");
string s1 = s.substr(6, string::npos); // 从下标为6的位置开始截取字符串到结尾

 find()

size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (const char* s, size_t pos, size_t n) const;
size_t find (char c, size_t pos = 0) const;string s("hello_world");
size_t pos = s.find('_', 0); // 从下标为0的位置开始找字符'_',找到了返回这个字符的下标,找不到返回npos的值

 rfind()

size_t rfind (const string& str, size_t pos = npos) const;	
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos, size_t n) const;
size_t rfind (char c, size_t pos = npos) const;string s("hello_world");
size_t pos = s.rfind('_'); // 从最后一个位置开始找,找不到返回npos

 find_first_of

size_t find_first_of (const string& str, size_t pos = 0) const;
size_t find_first_of (const char* s, size_t pos = 0) const;
size_t find_first_of (const char* s, size_t pos, size_t n) const;
size_t find_first_of (char c, size_t pos = 0) const;string str ("hello world");
size_t found = str.find_first_of("aeiou"); // 修改了文档的实例,匹配"aeiou"中的任意一个字符就可以
while (found!=std::string::npos)
{str[found]='*'; // 把这些字符替换成'*'found=str.find_first_of("aeiou",found+1);
}

getline

istream& getline (istream& is, string& str); // 相比较与cin,它可以输入带有空格符的字符串string s;
getline(cin, s);

string库中的函数

to_string

string to_string (int val); // 可以把int类型装换为string类型,不止是int、float、long等int n = 123456;
string s = to_string(n);

从字符串装换为其他类型

// 只举例了两种,还有很多种可以去看文档
string is = "1234";
string ds = "1234.00"
int n = stoi(is);
double d = stod(ds);

深浅拷贝

        在我们包含的string库中是不会出现这样的问题,别人都已经解决了,这个问题也不是很难。之前说的浅拷贝的问题是拷贝构造函数不写编译器会默认生成一个函数,按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,这样两个对象指向同一块空间,作用域结束后调用析构函数,这样同一块空间析构两次肯定会出问题,既然指向同一块空间出问题,那就再开一块空间分配个这个对象。

        赋值操作也会出问题,我们不写编译器会默认生成一个赋值运算符重载,这也是浅拷贝,会出现两个问题,第一个就是最后会析构两次,还有一个问题就是被赋值的对象原来所指向的空间丢失,这样会导致内存泄漏。

class string // 这里简单模拟实现一下string
{
public:string(const& string s) :_str(new char[s._capacity+1]) // 初始化列表,新开一块空间并且多开一个存放\0,_size(s._size),_capacity(s._capacity){strcpy(_str, s._str); // 再把s1的值拷贝过来}string& operator=(const satring& s) // 直接释放s1原来的空间,再申请一块和s3同样大小的空间就可以了{// 还要注意自己给自己赋值的情况,因为语法是支持这样做的// 如果不处理,开始就会直接释放自己的空间,这样也就没办法赋值了    if (this != &s){char* tmp = new char[s._capacity+1]; // +1位置存放\0, 先去开空间,保证空间开辟不出错,再去赋值和释放strcpy(tmp, s._str);delete[] _str; _str = tmp;_size = s._size;_capacity = s._capacity;}return *this; }
private:char* _str;size_t _size;size_t _capacity
};int main()
{string s1("hello");string s2(s1); // 用s1拷贝构造s2string s3("123456");s1 = s3; // 赋值操作也是有问题的,编译器生成的默认赋值,把s1指向s3指向的空间,但是原来s1指向的空间丢失,会造成内存泄露的问题return 0;
}

拷贝构造的另一种写法 

// string s2(s1)
string(const string& s):_str(nullptr),_size(0),_capacity(0)
{string tmp(s._str); // 这里调用了构造函数,开辟一块新的空间给tmp// 再把所有的成员变量交换,这样就把s1赋值给了s2,拷贝构造结束后,tmp会自动析构// 但是tmp交换完之后就是原来s2的值,s2原来没有开辟过,指向的空间是一块随机的// delete随机空间是会出错的,但是delete nullptr不会,所以在初始化列表中初始化一下swap(_str, tmp._str); swap(_size, tmp._size);swap(_capacity, tmp._capacity);
}

赋值运算符重载的另一种写法

// s1 = s2
string& operator=(const string& s)
{if (this != &s){string tmp(s);swap(_str, tmp._str);swap(_size, tmp._size);swap(_capacity, tmp._capacity);}return *this;
}// 还有一种
string& operator=(string s) // 这里的s是一个临时变量,是拷贝构造出来的,出了作用域也会自动释放
{swap(_str, s._str);swap(_size, s._size);swap(_capacity, s._capacity);return *this;
}

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

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

相关文章

腾讯云优惠券种类、领取方法及使用教程分享

腾讯云是国内领先的云计算服务提供商&#xff0c;为用户提供丰富的云计算产品和服务。为了吸引更多用户使用腾讯云的产品和服务&#xff0c;腾讯云会定期推出各种优惠券活动。本文将为大家介绍腾讯云优惠券的种类、领取方法及使用教程。 一、腾讯云优惠券种类介绍 腾讯云优惠券…

STM32 CubeMX ADC采集 单通道,多通道,内部温度(轮询,DMA,中断)(HAL库)

STM32 CubeMX ADC采集&#xff08;HAL库&#xff09; STM32 CubeMX STM32 CubeMX ADC采集&#xff08;HAL库&#xff09;ADC介绍ADC主要特征Vref的电压&#xff08;2.4~3.6&#xff09;就是ADC参考电压2.4V&#xff08;相当于秤砣&#xff09; 最小识别电压值&#xff1a;2.4/4…

goland 旧版本使用1.19环境

C:\Go\src\runtime\internal\sys\zversion.go // Code generated by go tool dist; DO NOT EDIT.package sysconst StackGuardMultiplierDefault 1const TheVersion go1.19引入其他包的标识符 package mainimport ("fmt""gotest/test")func main() {f…

Stm32_标准库_8_ADC_光敏传感器_测量具体光照强度

ADC简介 测量方式 采用二分法比较数据 IO通道 ADC基本结构及配置路线 获取数字变量需要用到用到光敏电阻的AO口&#xff0c;AO端口接在PA0引脚即可 测得的模拟数据与实际光照强度之间的关系为 光照强度 100 - 模拟量 / 40;代码&#xff1a; 完整朴素代码&#xff1a; #in…

leetcode-电话号码组合(C CODE)

1. 题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits “23” 输出&#…

超美!ChatGPT DALL-E 3已可用,另外GPT-4可上传图片进行问答

今天&#xff0c;在ChatGPT里使用DALL-E 3的功能终于上线了。以下是截图&#xff1a; 在GPT-4下加了一个菜单入口&#xff0c;名为 DALL-E 3&#xff0c;这也意味着ChatGPT免费账户暂时不能使用这个功能。 我们体验一下这个功能。 技术交流 建了技术交流群&#xff01;想要进…

mysql面试题44:MySQL数据库cpu飙升的话,要怎么处理?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:MySQL数据库cpu飙升的话,要怎么处理呢? 当MySQL数据库的CPU使用率飙升时,可能表示数据库负载过重或存在性能问题。以下是处理MySQL数据库CPU飙…

记一次生产大对象及GC时长优化经验

最近在做一次系统整体优化,发现系统存在GC时长过长及JVM内存溢出的问题,记录一下优化的过程 面试的时候我们都被问过如何处理生产问题&#xff0c;尤其是线上oom或者GC调优的问题更是必问&#xff0c;所以到底应该如何发现解决这些问题呢&#xff0c;用真实的场景实操&#xff…

基于Dockerfile搭建LNMP环境

准备工作 #关闭防火墙和防护机制 systemctl stop firewalld systemctl disable firewalld setenforce 0 docker network create --subnet172.18.0.0/16 --opt "com.docker.network.bridge.name""docker1" mynetwork#设置自定义网络模式&#xff0c;模…

【多线程案例】设计模式-单例模式

1.单例模式 什么是单例模式&#xff1f; 所谓单例&#xff0c;即单个实例。通过编码技巧约定某个类只能有唯一一个实例对象&#xff0c;并且提前在类里面创建好一个实例对象&#xff0c;把构造方法私有化&#xff0c;再对外提供获取这个实例对象的方法&#xff0c;&#xff0…

CSS 之 table 表格布局

一、简介 ​ 除了使用HTML的<table>元素外&#xff0c;我们还可以通过display: table/inline-table; 设置元素内部的布局类型为表格布局。并结合table-cell、table-row等相关CSS属性值可以实现HTML中<table>系列元素的效果&#xff0c;具有表头、表尾、行、单元格…

JS之同步异步promise、async、await

promise异步操作 Promise是异步编程的一种解决方案 JavaScript异步与同步解析 学习promise前我们先来了解下什么是异步&#xff1f; 基本概念&#xff1a; 消息队列中的任务分为宏任务与微任务;调用栈也可以称为主线程 首先我们要知道js是单线程语言&#xff0c;也就是说…