云计算百科
云计算领域专业知识百科平台

string类 手动实现

c++委员会在std命名空间中定义了string类 在类中实现了许多对外的很多接口 

之前试过了string类接口的使用 接下来自己来手动实现一下于这样的string类 并不是真的和库中的一样 只是大概实现一些功能 加深一下对其底层的理解

首先为了不和std中的string类冲突  我们自己定义一个命名空间xx在里面实现string类 

先实现构造 析构 和拷贝构造

在默认构造函数中用初始化列表对成员变量进行初始化 我这里是开始没有开额外空间 只有一个空间来存\\0

带参的构造函数   _str开的空间大小我们需要先知道传过来的ss的长度  然后开这个长度+1的空间(要存\\0)

这里需要注意 初始化列表初始化的顺序是先声明的先进行初始化 和这里的顺序无关 所以_str会先初始化 不能直接用_size   除非改变声明的顺序 空间开好后把ss的值拷贝给_str

也可以写成缺省的形式 就只用这一个了

拷贝构造类似 如果不自己写拷贝默认的拷贝是浅拷贝 不满足我们的需求

赋值重载=也都类似 但是要记得 delete原来的空间

迭代器

std中string类实际的迭代器还是很复杂的 这里就简单的来实现    

这里iterator就是char*类型的别名    begin返回第一个位置 end返回最后一个字符的后一个位置

在类内重定义的名字 在类外需要用string::来进行访问 当迭代器实现之后 范围for就也可以使用了 因为范围for的底层实现其实就是迭代器

npos

正常情况在类里面声明时候的初始值只在初始化列表中没有定义时候会看  但是对于静态的不能在初始化列表写又  但是在这里npos可以直接在这里初始化为-1   这是非常特殊的情况 只有在是static 且const 且整形类型才可以   这种非常特殊可能是为了 static const int N 然后这样用N定义数组的方式

 这里npos是size_t无符号整形  初始化为-1补码为最大值npos就是最大值   

这里就用正常的在类外定义static变量的方式 

对于短小频繁调用的函数,可以直接定义到类里面来实现,默认是inline

对于比较长的在类内声明 然后在string.cpp文件中来实现

string类对象的容量

这里注意 在string.cpp文件中 同样要把实现的功能放到和string.h同名的命名空间里面 在test测试文件中同样也是 编译器最后会把不同文件中同名的命名空间内容合起来  如果在string.cpp中不用命名空间 要实现每一个函数还需要加命名空间名字 我这里是xx::

resize及reserve实现

reserve当n<=capacity的时候不做处理   当大于的时候再进行扩容 开n+1个空间的新zz 把之前内容拷贝之后delete 原来的空间 再指向新的zz

resize 要分n<size的情况和n>size的情况 

当n<小于size的时候 也就是要把长度缩小至n 不删的内容下标从0到n-1 那么n的位置也就是\\0的位置 然后之后改变size就可以

当n>size的时候  先考虑空间的问题 长度要增长到为n 则预留n大小的空间 然后从size开始(之前\\0的位置)直到n-1位置也为替换的字符c  然后n位置设为\\0 

这里缺省值为‘\\0’ 要注意声明定义分离的时候缺省值在声明出写  定义不用写

string类对象的修改操作

插入

这里append仅仅只实现 插入字符串形式 insert提供插入字符和字符串的方式 +=实现字符和字符串形式

其实push_back 和+=一个字符是一样的  append一个字符串也和+=一个字符串是一样的 只需写一个另一个直接赋用就可以了

push_back +=字符

插入之前我们要先考虑空间问题  这里是一个一个字符插入 可能会被频繁调用 如果只是按需扩容的话会造成频繁扩容 所有这里采用二倍扩的方式

+=字符串 append

同样首先考虑空间扩容  因为这里插入的字符串不知道多长 所有我们按需扩容就可以 

然后将字符串的内容拷贝到末尾 然后改变size及最后加上\\0

insert 插入单字符

insert和其他插入不同的是可以选择插入的位置  为了防止越界访问 首先需要断言一下传过来的pos是否合法 因为是size_t类型 肯定不会小于0 只需再<=size就可以了  =size就是尾插

空间扩容问题和上面的尾插单字符一样

然后这里插入字符之前需要先将后面的内容整体向后移动一个位置   这里看移动不移动最后的\\0

如果开始就移动的话 最后就不需要额外加\\0了  如果没有移动 最后要在末尾加上 开始没有移动\\0后面移动过程中把\\0给替换掉后会显示字符串无效 

insert 插入字符串

断言和空间问题都和前面的一样 移动也和单字符插入的insert类型 移动之后进行拷贝要注意不能用strcpy  strcpy也会把字符串ss的\\0给拷贝 如果插入的位置靠前那么后面原来的内容就没了

删除

erase

同样pos需要断言一下 

然后要分情况考虑

从pos开始到最后一个字符的长度如果小于等于len  也就是说后面会直接删完 那么久不需要额外的移动了 pos位置为'\\0' 然后改变size就可以

当大于len的时候  pos之后剩余的元素就需要整体向左移动 剩余的个数为size-pos-len 移动的长度就是删的长度len

如下图针对第二种情况图来理解  从pos位置开始删两个元素 也就是后面最后会剩下3 4 5 这三个元素   这三个剩下的元素需要整体向左移动删掉的长度也就是len个位置 这样3到了1 4到了2  5到了3

查找 

这里注意 这块都是声明定义分离的  缺省值在声明处写 定义处不写

 find 实现查找 字符 和查找子串的功能  substr实现返回string类型的子串

find 字符

先断言让pos合法 然后直接从pos位置开始遍历 找到就返回pos位置 没有找到返回npos

find 子串

同样先断言 之后用strstr函数来判断是不是子串  是会返回位置的指针 不是返回nullpre  这里找到了返回时候返回的是ff-_str   这涉及到数组指针的知识 -str就是this指向string类的成员变量_str 这是一个字符数组  _str是首元素的地址  _str+加就是第几个元素的地址  那这个地址减去首元素的地址就是下标

substr返回string类型子串

len大于剩余个数的话先更新len

之后创建一个新的ss 预留len个空间 然后加上拷贝的内容  最后加个\\0

运算符重载

先实现<和== 实现用到的是strcmp函数 第一个小于第二个返回负值 等于会返回0 大于会返回正数

剩下的几个和日期类实现那里的一样 只需要复用<和==就可以了

流运算符重载

对于输出来说 在日期类那里使用的方式是将其定义为友元函数 这里采用范围for的方式

输入

在开始读取前我们先把原来s中的内容给清理掉

输入存在一些问题 cin输入时候遇到空格就会停止读取 所以这里用到了cin里面的get get可以读取空格 

然后这里做了一些优化 

当要读取的数据非常多的时候就会频繁地扩容   这里我们相当于用数组的方式来做了一个缓冲 在数据进s之前先把数据读进数组里面 每当读的数据够了200再往s里面放  直到数据不够200数组后面放个\\0再给s 

如果没有这个优化 刚开始的空间就会频繁扩容 一个一个数据来看  扩容情况就会是  4 8 16 32 64 128 这样刚开始空间小就会频繁地扩容  有个这个优化后 刚开始空间是0 数组够了200之后给s 跳到+=函数 识别到200字符 直接预留200空间 就大大减少了扩容的次数 

赞(0)
未经允许不得转载:网硕互联帮助中心 » string类 手动实现
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!