顺德网站建设公司价位/神马搜索seo优化排名
文章目录
- 前言
- string的成员变量
- 成员函数
- 构造函数
- 拷贝构造
- 赋值重载
- 模拟实现string各种接口
- 迭代器
- 普通迭代器
- const迭代器
- string比较大小
- push_back
- insert 和 erase
- insert
- erase
- reserve和resize
- reserve
- resize
- swap
- find
- cout和cin
- cout
- cin
前言
今天要讲string的底层实现,通过自己来实现string,我们对string的理解才能更加的深刻。
我们对string其实既熟悉又陌生,熟悉sting其实就是字符串,陌生是在于管理字符串这样一个类。
string的成员变量
namespace but
{class string{private:char* _str;size_t _capaicty;size_t _size;};
}
我们为了避免自己定义的string于库里面的傻傻分不清,这里我们自己用了一个命名空间把自己写的string封装起来。
成员函数
构造函数
namespace but
{class string{public:string():_str(nullptr),_capaicty(0),_size(0){}string(const char* str):_str(str),_capaicty(strlen(str)),_size(strlen(str))//容量不包括'\0'{}private:const char* _str;//加上const,防止写构造函数时,权限放大编译不通过size_t _capaicty;size_t _size;};
}
简简单单写了上面的构造函数,其实这里面存在两个问题,下面我们通过一些使用来看一下。
第一个问题。
写个c_str,思考一下为什么程序会崩?
const char* c_str()
{return _str;
}
string s1;
string s2("hello world");
cout << s1.c_str() << endl;//上述代码都是写在类里面
cout << s2.c_str() << endl;
流插入是自动识别类型,它识别出const char*, 然后去解引用,然后遇到‘\0’结束,这样空指针的问题就暴露出来了。
继续看第二个问题
const char& operator[](size_t pos)//按照之前写的构造函数,必须加上const
{assert(pos < _size);return _str[pos];
}
这里面有个很坑的问题,我们是呆会是要修改pos位置的字符,并且如果空间不够还需要扩容,比如+=;那这里就变得非常矛盾。
这是什么原因呢?
string s2("hello world");
s2在常量区无法修改,扩容也无法扩。
如何解决这两个问题呢?
其实根源还是在于初始化列表,我们大多数情况下都是推荐把所有成员变量直接放到初始化列表初始化,这里比较特殊。
其次,我们要想修改pos位置的字符,还想扩容,在初始化的时候空间就不能直接赋值过去,最好new出来。
那经过修改之后,我们的代码
namespace but
{class string{public:string():_str(new char[1]),//要解决第一个问题,这里就不能是空_capacity(0),_size(0){_str[0] = '\0';}string(const char* str):_capacity(strlen(str)){_size = _capacity;//没有必要重复用strlen,strlen是o(N)的接口_str = new char[_capacity + 1];//扩容的时候应该+1,包括\0strcpy(_str, str);}~string(){delete[] _str;_str = nullptr;_capacity =_size= 0;}const char* c_str(){return _str;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}private:char* _str;//加上const,防止写构造函数时,权限放大编译不通过size_t _capacity;size_t _size;};void test_string1(){string s1;string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;s2[0]++;cout << s2.c_str() << endl;}
}
至此,把上面的问题都解决了。
拷贝构造还可以继续优化一下,优化成只有一个全缺省的构造函数。
//string(const char* str = nullptr) //不可以,等下strlen解引用会崩
//string(const char* str = '\0')//不可以,类型不匹配
//string(const char* str = "\0")//可以
string(const char* str = "")//可以:_size(strlen(str))
{_capaicty = _size == 0 ? 3 : _size;_str = new char[_capaicty + 1];strcpy(_str, str);
}
拷贝构造
void test_string2()
{string s2("hello world");string s3(s2);cout << s2.c_str() << endl;cout << s3.c_str() << endl;
}
我们之前说过拷贝构造是默认成员函数,我们不写,编译器会自动生成一个,对自定义类型不做处理,对内置类型做值拷贝或浅拷贝。那我们看一下自动生成的拷贝构造。
这个是经典的值拷贝或浅拷贝问题,我们之前也讲过,接下来既然有一个具体的场景,就用调试带大家看一下。
看两个地址完全一摸一样。
这样会带来两个问题。
1.一个修改影响另外一个。
2.同一块空间会析构两次。
那我们需要自己写一个深拷贝的拷贝构造,怎么写呢?
//拷贝构造也有初始化列表
string(const string& s):_size(s._size), _capaicty(s._capaicty){_str = new char[s._capaicty + 1];strcpy(_str, s._str);}
赋值重载
赋值重载和拷贝构造也一摸一样,我们不写的话,编译器自动生成的会出问题。
写成这样,那就考虑的太不全面了
string& operator=(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[s._capacity + 1];strcpy(_str, s._str);return *this;
}
我们知道拷贝构造是一块已经存在的空间给另一块还没存在的空间。
而赋值重载是两块都已经存在的空间,所以赋值重载还需要从空间的角度去分析问题。
从空间大小考虑,总共有三种情况
但是存在一个问题,如果s3空间特别大,s1又非常小,把s1直接赋值过去,s3就会浪费很多空间,所以比较好的方式就是再开一块空间。
我们库里面的string实现不会这么麻烦,直接把旧的空间释放掉,开一块一样大的空间。
还要处理自己给自己赋值,以免造成不必要的麻烦。
string& operator=(const string& s){if (this != &s){//这种写法稍微不好一点//抛异常的时候会把s1给破坏掉/*delete[] _str;_str = new char[s._capaicty + 1];strcpy(_str, s._str);_size = s._size;_capaicty = s._capaicty;*/char* tmp = new char[s._capaicty + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capaicty = s._capaicty;}
模拟实现string各种接口
这里为什么报错?
这也涉及到我们之前讲过的。** cosnt成员变量不能调用非const成员函数,这样会权限放大。**
紧接着这里报错又怎么解决?
这说明我们需要两个【】,一个是给const对象调用的,不允许修改。
一个是给普通对象调用的,可以修改。它们构成函数重载,因为它们函数名相同参数不一样。
虽然普通对象也可以调用const成员函数,但是编译器非常聪明,他会调用最匹配的哪个。
迭代器
遍历的方式我们还可以用迭代器,这里我们再写一个迭代器
普通迭代器
要实现一个迭代器其实不难。
我们支持了迭代器,其实也就支持了范围for
for (auto ch : s1)
{cout << ch << " ";
}
const迭代器
const迭代器能不能修改?
可以修改,只是指向的内容不能修改。
string::const_iterator it = s1.begin();
while (it != s1.end())
{//*it = 'x';//不能修改,只能读不能改++it;
}
cout << endl;
反向迭代器这里先不讲,后面再讲,要用一个适配器来实现。
string比较大小
怎样比较大小?
比较ascll值,一个一个比。
// 不修改成员变量数据的函数,最好都加上constbool operator>(const string& s) const{return strcmp(_str, s._str) > 0;}bool operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool operator>=(const string& s) const{//return *this > s || *this == s;return *this > s || s == *this;}bool operator<(const string& s) const{return !(*this >= s);}bool operator<=(const string& s) const{return !(*this > s);}bool operator!=(const string& s) const{return !(*this == s);}
push_back
空间不够扩容的时候不能用realloc,那就和c++交叉了,容易出问题。
void push_back(char ch)
{if (_size + 1 > _capaicty){reserve(_capaicty * 2);}_str[_size] = ch;++_size;_str[_size] = '\0';
}void append(const char* str)
{size_t len = strlen(str);if (_size+len > _capaicty){reserve(_size + len);}strcpy(_str + _size, str);//strcat(_str, str);//为什么不用strcat?strcat很挫自己要去找\0,\0就在size位置,能不用就不用_size += len;
}
我们喜欢使用的还是+=,直接复用push_back;
string& operator+=(char ch)
{push_back(ch);return *this;
}string& operator+=(const char* str)
{append(str);return *this;
}
凡是你的扩容,析构上代码崩了,一般都是内存问题。
insert 和 erase
问个小小的问题,静态成员变量能不能给缺省值?
不能,因为缺省值是给初始化列表用的。静态列表不是在初始列表初始化的。
它属于整个类,不是属于某个对象。
insert
插入字符
insert有个巨坑给大家看一下下面的代码?
程序运行结果。
调试的时候发现这样,扯淡了。
因为end的类型是size_t;
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size + 1 > _capacity){reserve(2 * _capacity);}//int end=_size;//这样也不行,会发生类型转换,一般有符号转化为无符号。//改pos也不好,pos的类型一般规定都是size_tsize_t end = _size;//while(end>=pos(int))//强转也不推荐//while (end >= pos)//{// _str[end + 1] = _str[end];// --end;//}size_t end = _size + 1;while (end > pos){_str[end] = _str[end-1];--end;}_str[pos] = ch;++_size;
}
我们最好的解决思路,巧妙的避开了小于0;
插入字符串
一定要画图,不然很容易出错。
string& insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}// 挪动数据size_t end = _size + len;while (end > pos + len - 1)//强烈不建议用大于等于{_str[end] = _str[end - len];--end;}//这个比较简单,完美避开了循环结束条件的难题/*size_t end = _size;for (size_t i = 0; i < _size + 1; ++i){_str[end + len] = _str[end];--end;}*/// 拷贝插入strncpy(_str + pos, str, len);_size += len;return *this;}
erase
erase比较简单,从pos位置删除数据就可以了。
我们浅浅分析一下所有的情况
erase也是不考虑缩容的。
string& erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);//不需要考虑覆盖的问题,所以可以直接用strcpy_size -= len;}return *this;}
白盒测试,把三种情况都验证一遍
reserve和resize
reserve
看一下我们之前写的扩容有什么问题?
它是没有考虑缩容的。继续看这样子就报错了。
为什么报错呢?
strcp的时候越界了。
简单修改一下代码就变成这样了。
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
resize
resize缩容吗?
不缩容。缩荣的代价还是很大的,首先是异地缩,先开另一块空间,然后把数据拷贝过去,接着把之前的空间释放掉。
待会插入数据空间不够又要扩容,这样就很麻烦。
接下来实现resize,我们得分情况讨论,以及明白resize功能上的一些细节。
void resize(size_t n, char ch = '\0')
{if (n < _size){// 删除数据--保留前n个_size = n;_str[_size] = '\0';}else if (n > _size){if (n > _capacity){reserve(n);}//如果调用系统的接口,我们可以用memsetsize_t i = _size;while (i < n){_str[i] = ch;++i;}_size = n;_str[_size] = '\0';}
}
swap
我们实现 一下swap,其实就知道库里面的swap和类里面的效率差距有多大
//swap(s1, s2);
//s1.swap(s2);
void swap(string & s)
{std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);
}
find
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);char* p = strstr(_str + pos, str);if (p == nullptr){return npos;}else{return p - _str;}}
cout和cin
现在我有一个问题,cout和cin必须实现成友元函数?这句话对不对
不对,我们可以写一些函数来访问私有成员变量。
cout
首先,我们实现cout, 它不是成员函数。
能不能直接这样搞?
我们之前说过c_str()和cout是有区别的,它们最大的区别就是c_str()打印时是遇到\0终止,cout是根据size来打印的。
ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}
cin
这样为什么不行?
调试一下就知道了,空格和换行不会进入缓冲区。为什么?
它会认为你输入的时候多个字符之间的间隔。
我们可以改成这样
仔细看一下上面的代码,功能是完善了但还有什么弊端。
这是没有把之前的数据清理掉。
还有一个问题有个流插入的数据比较长,那它会影响效率,那有没有什么方法能解决这个问题?
开小了不够,开多了浪费。这里有一个参考方式。
相当于换成字符串,可以这样理解。
istream& operator>>(istream& in, string& s)
{s.clear();char ch = in.get();char buff[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}//防止还有数据没有+=进去if (i != 0){buff[i] = '\0';s += buff;}return in;
}
相关文章:

C++STL的string模拟实现
文章目录 前言string的成员变量成员函数构造函数拷贝构造赋值重载 模拟实现string各种接口print迭代器普通迭代器const迭代器 string比较大小push_backinsert 和 eraseinserterase reserve和resizereserveresize swapfindcout和cincoutcin 前言 今天要讲string的底层实现&…...

基于ZLMediaKit的webrtc实时视频传输demo搭建
环境 ubuntu 20.04 gcc version 9.4.0 cmake version 3.16.3 部署ZLMediaKit流媒体服务器 安装openssl 首先可以检查一下自己的openssl的版本如果是1.1.1以上就可以忽略这一步 wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz tar -xvzf openssl-1.1.1k…...

LeetCode双指针:有序数组中的单一元素
LeetCode双指针:有序数组中的单一元素 题目描述 给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。 请你找出并返回只出现一次的那个数。 你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复…...

熬夜会秃头——Beta冲刺总结随笔
这个作业属于哪个课程2301-计算机学院-软件工程社区-CSDN社区云这个作业要求在哪里团队作业—beta冲刺事后诸葛亮-CSDN社区这个作业的目标总结Beta冲刺团队名称熬夜会秃头团队置顶集合随笔链接熬夜会秃头——Beta冲刺置顶随笔-CSDN社区 目录 一、Beta冲刺开始前设立的任务完成…...

C++函数模板案例
利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序排序规则从大到小,排序算法为选择排序分别利用char数组和int数组进行测试 #include<iostream> using namespace std;template<class T> void myswap(T& a, T& b) {T…...

同旺科技 USB TO RS-485 定制款适配器--- 拆解(三)
内附链接 1、USB TO RS-485 定制款适配器 ● 支持USB 2.0/3.0接口,并兼容USB 1.1接口; ● 支持USB总线供电; ● 支持Windows系统驱动,包含WIN10 / WIN11系统32 / 64位; ● 支持Windows RT、Linux、Mac OS X、Windo…...

Vue学习计划-Vue2--Vue核心(六)过滤器和自定义指令
1. 过滤器 定义:对要显示的数据进行特定格式转换再显示(适用于一些简单逻辑的处理)语法: 注册过滤器:Vue.filter(name, callback) 或 new Vue{filters:{}}使用过滤器:{{ xx | 过滤器名 }} 或 v-bind:属性 …...

Codeforces Round 913 (Div. 3) (A-G)
后天就是 I C P C ICPC ICPC杭州站了,今天把之前做的 d i v 3 div3 div3题补一下,打完这场杭州站这赛季除了 E C F i n a l EC\,\,Final ECFinal就结束了,以后应该要多打 c f cf cf比赛练习保持手感,争取下赛季冲一下金牌。 感觉这…...

CSS——sticky定位
1. 大白话解释sticky定位 粘性定位通俗来说,它就是相对定位relative和固定定位fixed的结合体,它的触发过程分为三个阶段 在最近可滚动容器没有触发滑动之前,sticky盒子的表现为相对定位relative【第一阶段】, 但当最近可滚动容…...

Redis hash表源码解析
1、 整体数据结构 链式hash解决hash冲突、采用渐进式hash来完成扩容过程。 /** 哈希表节点*/ typedef struct dictEntry {// 键void *key;// 值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下个哈希表节点,形成链表struct dictEntry *next;} dictEntry;…...

dll动态链接库【C#】
1说明: 在C#中,dll是添加 【类库】生成的。 2添加C#的dll: (1)在VS中新建一个Windows应用程序项目,并命名为TransferDll。 (2)打开Windows窗体设计器,从工具箱中为窗体…...

Linux 系统设置cpu频率
source_code: https://github.com/emagii/cpufrequtils cpufreq-set - A small tool which allows to modify cpufreq settings.(修改内存频率的工具) cpufreq-set allows you to modify cpufreq settings without having to type e.g. “/sys/devices…...

git基本概念
一、版本控制概念 1.1 什么是版本控制 1.1.1 手动管理文件版本 1.1.2 版本控制软件 概念:版本控制软件是一个用来记录文件发生的变化,以便将来查阅特定版本修订情况的系统,有时也叫“版本控制系统”。通俗的理解就是把手工管理文件版本的方…...

多个HTML属性
在HTML中,属性用于提供有关HTML元素的附加信息。在这篇文章中你将学习多个HTML属性,它们可以增强网站的视觉吸引力。 接下来开始吧!🚀 Accept 属性 您可以将accept属性与<input>元素(仅用于文件类型ÿ…...

基于运算放大器的电压采集电路
一、运算放大器 运放推导的两个重要概念:虚短、虚断。 1、差分放大器 以差分放大器为例进行推导分析。 虚断–运放的"-“端、”“端的引脚电流接近为0; 根据基尔霍夫电流定律可知:iR1iRF,iR2iR3; iR1(Ui1-(V-…...

数字图像处理(实践篇) 十六 基于分水岭算法的图像分割
目录 一 分水岭算法 二 利用OpenCV实现分水岭算法的过程 三 实践 一 分水岭算法 基于任何灰度图像都可以视为地形表面,其中高强度表示山峰和山丘,而低强度表示山谷。首先,开始用不同颜色的水(标签)填充每个孤立的山…...

快速学习PyQt5的高级自定义控件
Pyqt5相关文章: 快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图(Item View) 快速弄懂Pyqt5的4种项目部件(Item Widget) 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的10种容器&…...

Python中读写(解析)JSON文件的深入探究
目录 一、引言 二、如何读取JSON文件 三、如何写入JSON文件 四、如何解析JSON字符串 五、错误处理和异常处理 六、使用第三方库提高效率 七、总结 一、引言 在Python中,我们经常使用JSON(JavaScript Object Notation)格式来存储和传输…...

我获取股票和期货数据的常用函数
记录一下获取数据所使用的函数,以防止遗忘和方便查找。 # 获取掘金的数据 # 需要打开并登陆掘金终端 def get_data_juejin(symbol"bu2112",start"2021-8-1",end"2021-8-30 23:00:00",frequency"1800s",fields"eob,sy…...

高并发场景下的httpClient使用优化技巧
1. 背景 我们有个业务,会调用其他部门提供的一个基于http的服务,日调用量在千万级别。使用了httpclient来完成业务。之前因为qps上不去,就看了一下业务代码,并做了一些优化,记录在这里。 先对比前后:优化…...

用php上传图片到阿里云oss
如果你想自动创建目录并将文件上传到新的目录下,你可以使用阿里云 OSS 的 createObject 方法来实现。下面是更新后的示例代码: php <?php require_once __DIR__ . /vendor/autoload.php; // 引入 SDKuse OSS\OssClient; use OSS\Core\OssException;…...

服务器适合哪些使用场景_Maizyun
服务器适合哪些使用场景 在当今的数字化时代,服务器作为互联网基础设施的核心组件,被广泛应用于各种场景。本文将探讨服务器适合哪些使用场景。 一、Web服务器 Web服务器是服务器中最常见的一种,用于托管和运行网站。它负责处理来自客户端…...

发布“最强”AI大模型,股价大涨,吊打GPT4的谷歌股票值得投资吗?
来源:猛兽财经 作者:猛兽财经 谷歌在AI领域的最新进展,引发投资者关注 在谷歌-C(GOOGL)谷歌-A(GOOG)昨日发布了最新的AI大模型Gemini后,其股价就出现了大幅上涨,更是引发了投资者的密切关注&a…...

年度工作总结怎么写?掌握这些年终总结万能公式,让你的报告出彩无比!
光阴似箭,日月如梭,时间总是不疾不徐地向前奔去,转眼就来到了2023年的最后一个月,12月一到,上班族和打工人又要开始忙活工作总结的事情~ 工作总结,不仅是一年工作的回顾,更是未来规划的起点。你…...

常用Nmap脚本
端口扫描类脚本 Nmap是一款非常流行的端口扫描工具,它可以帮助渗透测试工程师识别目标网络上开放的端口,并提供有关这些端口的详细信息。Nmap还提供了一系列基于脚本的功能,这些脚本可以扩展Nmap的功能,使其能够更深入地探测目标网…...

在pom.xml中添加maven依赖,但是类里面import导入的时候报错
问题: Error:(27, 8) java: 类TestKuDo是公共的, 应在名为 TestKuDo.java 的文件中声明 Error:(7, 23) java: 程序包org.apache.kudu不存在 Error:(8, 23) java: 程序包org.apache.kudu不存在 Error:(9, 23) java: 程序包org.apache.kudu不存在 Error:(10, 30) jav…...

【NEON】学习资料汇总
一、资料链接 Guide : http://www.heenes.de/ro/material/arm/DEN0018A_neon_programmers_guide_en.pdf csdn博文1,基础案例: https://blog.csdn.net/kakasxin/article/details/103912832? csdn博文2,内部函数: ht…...

java中ReentrantLock的实现原理是什么?
ReentrantLock 的实现原理主要涉及到两个关键概念:同步器(Sync)和 AQS(AbstractQueuedSynchronizer,抽象队列同步器)。 ReentrantLock 使用 AQS 来实现可重入锁的机制。AQS 是 Java 并发包中的一个抽象基类…...

C语言精选——选择题Day40
第一题 1. int a[10] {2,3,5}, 请问a[3]及a[3]之后的数值是() A:不确定的数据 B:5 C:0 D:0xf f f f f f f f 答案及解析 C 数组的不完全初始化,会自动把没初始化的部分初始化为0; 第…...

大屏适配方案一scale()
背景 在做大屏可视化项目的时候,一般设计稿会设计成1920 * 1080,但是页面写死1920 * 1080在2k、4k等分辨率的屏幕下是不适配的。 方案一:css3的缩放属性transform以及scale() 在做项目之前我们需要搞清楚客户的数据可视化平台需要在什么屏幕…...