c++初阶知识——string类详解

 

目录

 

前言:

1.标准库中的string类

1.1 auto和范围for

auto

 范围for

1.2 string类常用接口说明

1.string类对象的常见构造

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

1.4. string类对象的修改操作 

 1.5 string类非成员函数

2.string类的模拟实现 

2.1 经典的string类问题 

2.2 浅拷贝 

2.3 深拷贝 

2.4 string类实现 

3.写时拷贝 


前言:

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

1.标准库中的string类

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

1.1 auto和范围for

auto

(1)在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期
推导而得

(2)用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

(3)当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

(4)auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

(5)auto不能直接用来声明数组

#include <map>
using namespace std;
int main()
{
std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange",
"橙子" }, {"pear","梨"} };
// auto的用武之地
//std::map<std::string, std::string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
范围for
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此
C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围
内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
范围for可以作用到数组和容器对象上进行遍历
范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
2.3 string类的常用接口说明(注意下面我只讲解最常用的接口)
1. string类对象的常见构造
return 0;
}

 范围for

(1)对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

(2)范围for可以作用到数组和容器对象上进行遍历

(3)范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

示例:

#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{int array[] = { 1, 2, 3, 4, 5 };// C++98的遍历for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){array[i] *= 2;}for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){cout << array[i] << endl;}// C++11的遍历for (auto& e : array)e *= 2;for (auto e : array)cout << e << " " << endl;string str("hello world");for (auto ch : str){cout << ch << " ";}cout << endl;
return 0;
}

1.2 string类常用接口说明

1.string类对象的常见构造

注意:
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不会改变容量大小。 

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

1.4. string类对象的修改操作 

注意
1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差
不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可
以连接字符串。


2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留
好。 

 1.5 string类非成员函数

2.string类的模拟实现 

2.1 经典的string类问题 

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

// 为了和标准库区分,此处使用String
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;
};
// 测试
void TestString()
{
String s1("hello bit!!!");
String s2(s1);
}

 

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

2.2 浅拷贝 

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

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一
不想分享就你争我夺,玩具损坏。

所以可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。 

2.3 深拷贝 

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

 

 

2.4 string类实现 

 能否写好string反映出我们对类和对象知识的理解是否深刻,这一块知识如果理解得不够深刻,我们的c++程序就会经常出现此类问题。为了方便管理,我们将string的实现分为3个文件是实现:

string.h :

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace Myobject
{class string{public:typedef char* iterator;typedef const char* const_iterator;string& operator+=(char ch);string& operator+=(const char* str);void append(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 len = npos);size_t find(char ch, size_t pos);size_t find(const char* str, size_t pos);string substr(size_t pos, size_t len);string& operator=(const string& s);string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}void test0_01();void reserve(size_t n);void push_back(char ch);/*string():_str(new char[1] {'\0'}),_size(0),_capacity(0){}*/iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}const char* c_str() const{return _str;}size_t size(){return _size;}size_t capacity(){return _capacity;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const	char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;size_t _size;size_t _capacity;static const size_t npos;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);
}

string.cpp :

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"namespace Myobject
{const size_t string::npos = -1;string string::substr(size_t pos, size_t len){assert(pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}size_t string::find(char ch, size_t pos){assert(pos < _size);for (size_t i = 0; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){assert(pos < _size);const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;}}void string::erase(size_t pos, size_t len){assert(pos < _size);if (len >= _size - pos){_str[pos] = '0';_size = pos;}else{for (size_t i = pos + len; i < _size; i++){_str[i - len] = _str[i];}_size -= len;}}void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;if (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;}//插入单个字符void string::insert(size_t  pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size == 2 * _capacity ? len + _size : 2 * _capacity);}size_t end = _size + len;while (end - 2 > pos){_str[end] = _str[end - len];end--;}for (int i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}void string::append(const char* str){size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size == 2 * _capacity ? len + _size : 2 * _capacity);}strcpy(_str + _size, str);_size += len;//	_str[_size] = '\0';}string& string::operator+=(const char* str){append(str);return *this;}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;_size++;_str[_size] = '\0';}//尾插string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator=(const string& s){if (this != &s){delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;return *this;}}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){char ch;ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}
}

test.cpp :

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"namespace Myobject
{void test_01(){string s1;string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;for (int i = 0; i < s2.size(); i++){s2[i] += 2;}cout << s2.c_str() << endl;s2 += 'A';s2 += 'B';string::iterator it = s2.begin();while (it != s2.end()){cout << *it << " ";it++;}cout << endl;for (auto ch : s2){cout << ch << " ";}cout << endl;s2.insert(0, '$');for (auto ch : s2){cout << ch << " ";}cout << endl;s2.insert(8, "%%%%%%%");for (auto ch : s2){cout << ch << " ";}cout << endl;s2.erase(8, 100);for (auto ch : s2){cout << ch << " ";}cout << endl;/*s2.append("hehe");for (auto ch : s2){cout << ch << " ";}s2 += "hello";for (auto ch : s2){cout << ch << " ";}*/}void test02(){string s1("hello world");string s2 = s1.substr(6, 5);cout << s2.c_str() << endl;string s3("hello bit");s2 = s3;cout << s1 << endl;cout << s2 << endl;cin >> s1;cout << s1 << endl;}
}
int main()
{Myobject::test02();//Myobject::test_01();return 0;
}

3.写时拷贝 

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该
资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有
其他对象在使用该资源。

本章完。 

 

 

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

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

相关文章

GAMES104:05游戏引擎中的渲染系统2:渲染中的光照、材质和shader-学习笔记

文章目录 一、渲染方程及其挑战二、基础光照解决方案-简化版简化光源简化材质简化阴影 三、基于预计算的全局光照3.1挑战和计算思路傅里叶变换球谐函数&#xff08;Spherical Harmonics&#xff09; 3.2 SH Lightmap&#xff1a;预计算 GI3.3 探针 Probe&#xff1a;Light Prob…

无涯·问知财报解读,辅助更加明智的决策

财报解读就像是给公司做一次全面的体检&#xff0c;是理解公司内部运作机制和市场表现的一把钥匙&#xff0c;能够有效帮助投资者、分析师、管理层以及所有市场参与者判断一家公司的健康程度和发展潜力。 星环科技无涯问知的财经库内置了企业年报及财经类信息&#xff0c;并对…

Linux:传输层(1) -- UDP协议

1. 端口号 同一台主机的不同端口号(Port)标记了主机上不同的进程&#xff0c;如下图所示&#xff1a; 在 TCP/IP 协议中 , 用 " 源IP", "源端口号", "目的IP", "目的端口号", "协议号" 这样一个五元组来标识一个通信 ( 可…

制作excel模板,用于管理后台批量导入船舶数据

文章目录 引言I 数据有效性:基于WPS在Excel中设置下拉框选择序列内容II 数据处理:基于easyexcel工具实现导入数据的持久化2.1 自定义枚举转换器2.2 ExcelDataConvertExceptionIII 序列格式化: 基于Sublime Text 文本编辑器进行批量字符操作引言 需求: excel数据导入模板制…

基于 HTML+ECharts 实现的大数据可视化平台模板(含源码)

构建大数据可视化平台模板&#xff1a;基于 HTML 和 ECharts 的实现 大数据的可视化对于企业决策、市场分析和业务洞察至关重要。通过直观的数据展示&#xff0c;团队可以快速理解复杂的数据模式&#xff0c;发现潜在的业务机会。本文将详细介绍如何利用 HTML 和 ECharts 实现一…

js逆向——origin/refer请求头反爬

今日受害网站&#xff1a; https://www.regulations.gov/docket/FDA-2016-D-1399/document 最终目标&#xff1a;爬取该网站中的新闻摘要 首先打开网页&#xff0c;刷新一下&#xff0c;观察都返回了哪些数据 然后我们ctrlf进行关键字搜索 进一步&#xff0c;只过滤含有docu…

Linux_实现TCP网络通信

目录 1、实现服务器的逻辑 1.1 socket 1.2 bind 1.3 listen 1.4 accept 1.5 read 1.6 write 1.7 服务器代码 2、实现客户端的逻辑 2.1 connect 2.3 客户端代码 3、实现服务器与客户端的通信 结语 前言&#xff1a; 在Linux下&#xff0c;实现传输层协议为TCP…

MySQL数据库-备份恢复

一、MySQL日志管理 1.为什么需要日志 用于排错用来做数据分析了解程序的运行情况&#xff0c;了解MySQL的性能 2.日志作用 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;就必须保证数据的安全性和完整性&#…

鸿蒙SDK开发能力

什么是鸿蒙SDK&#xff1a;HarmonyOS(Software Development Kit)是面向应用和服务开发的开放能力合集,本质就是工具集&#xff0c;与JDK、AndroidSDK在逻辑上有相似之处 18N&#xff1a;1指的是手机&#xff0c;8指的是车机、音箱、耳机、手表/手环、平板、大屏、PC、AR/VR&am…

PCL-基于超体聚类的LCCP点云分割

目录 一、LCCP方法二、代码实现三、实验结果四、总结五、相关链接 一、LCCP方法 LCCP指的是Local Convexity-Constrained Patch&#xff0c;即局部凸约束补丁的意思。LCCP方法的基本思想是在图像中找到局部区域内的凸结构&#xff0c;并将这些结构用于分割图像或提取特征。这种…

DolphinScheduler学习

1.查看文档 点击访问&#xff1a;https://dolphinscheduler.apache.org/zh-cn/docs 我们可以看到相关的文档简介里有 介绍 DolphinScheduler是Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化…

太原高校大学智能制造实验室数字孪生可视化系统平台建设项目验收

随着科技的不断进步&#xff0c;智能制造已经成为推动制造业转型升级的重要力量。太原高校大学智能制造实验室紧跟时代步伐&#xff0c;积极推进数字孪生可视化系统平台的建设&#xff0c;并于近日圆满完成了项目的验收工作。这一里程碑式的成果&#xff0c;不仅标志着实验室在…

uniapp安卓plus原生选择系统文件

uniapp安卓plus原生选择系统文件 效果&#xff1a; 组件代码&#xff1a; <template xlang"wxml" minapp"mpvue"><view></view> </template> <script>export default {name: file-manager,props: {},data() {return {is…

靶场实战 _ ATTCK 实战 Vulnstack 红队

环境配置 网络拓扑图 (仅供参考) 攻击机&#xff1a;kali ip:192.168.111.5靶机&#xff1a;web-centos 外网ip:192.168.111.10 内网ip:192.168.93.100web1-ubuntu ip: 192.168.93.120PC ip: 192.168.93.30win 2008 ip:192.168.93.20win 2012 ip:192.168.93.10 信息搜集 端口…

【C++】string类(下)

个人主页~ string类&#xff08;上&#xff09; string类 二、模拟实现string类1、头文件string.h2、常见构造3、容量函数4、访问及遍历5、类对象修改6、流插入流提取重载 二、模拟实现string类 今天我们来实现一下上篇文章中详细介绍过的接口 1、头文件string.h #pragma onc…

Redis的应用场景及类型

目录 一、Redis的应用场景 1、限流 2、分布式锁 3、点赞 4、消息队列 二、Redis类型的命令及用法 1、String类型 2、Hash类型 3、List类型 4、Set类型 5、Zset类型 6、Redis工具类 Redis使用缓存的目的就是提升读写性能 实际业务场景下&#xff0c;我们就可以把 Mys…

【常微分方程】

框架 常微分方程的概念一阶微分方程可变离分量齐次方程一阶线性微分方程可降阶的高阶微分方程二阶常系数齐次线性微分方程二阶常系数非齐次线性微分方程 讲解 【1】 常微分方程&#xff1a;是微分方程的特殊情况&#xff1b; 阶&#xff1a;是方程未知函数的最高阶导数的阶数&…

ElementUI,修改el-table中的数据,视图无法及时更新

需求&#xff1a;点击table表格中的“修改”之后&#xff0c;当前行变为可输入状态的行&#xff0c;点击“确定”后变为普通表格&#xff1b; 先贴上已经完美解决问题的代码 实现代码&#xff1a; <section><div style"display: flex;justify-content: space-b…

爬虫学习1:初学者简单了解爬虫的基本认识和操作(详细参考图片)

爬虫 定义&#xff1a;爬虫&#xff08;Web Crawler 或 Spider&#xff09;是一种自动访问互联网上网页的程序&#xff0c;其主要目的是索引网页内容&#xff0c;以便搜索引擎能够快速检索到相关信息。以下是爬虫的一些关键特性和功能&#xff1a; 自动化访问&#xff1a;爬虫能…

【React】事件绑定:深入解析高效处理用户交互的最佳实践

文章目录 一、什么是事件绑定&#xff1f;二、基本事件绑定三、绑定 this 上下文四、传递参数五、事件对象六、事件委托七、常见事件处理八、优化事件处理 React 是现代前端开发中最受欢迎的框架之一&#xff0c;其组件化和高效的状态管理能力使得构建复杂的用户界面变得更加容…