【C++】string 类的实现

news2024/11/20 14:23:04

目录

  • 构造函数
  • 赋值重载
    • 关于浅拷贝
  • 迭代器
  • 容量相关
    • reserve
    • resize
  • 修改
    • push_back
    • append
    • insert
    • erase
      • 关于npos
  • 流运算符重载
    • 流插入
    • 流提取

构造函数

无参数构造和传参构造

  • 通过对参数设置缺省值为空串""同时满足无参构造和传参构造
  • 成员 _size 和 _capacity 均是针对有效位的
  • 开辟空间时除有效位外,要多预留一字节空间给 ‘\0’
string(const char* str = "")
	:_size(strlen(str))
{
	//注意size和capacity均是针对有效位的,
	//但是有效位占空间时要多预留一位给'\0'
	//多的一位只通过开空间时进行预留
	_capacity = _size == 0 ? 4 : _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

string(const string& s)
	: _size(s.size())
	, _capacity(s.capacity())
{
	_str = new char[s.capacity() + 1];
	strcpy(_str, s._str);
}

赋值重载

关于浅拷贝

若不主动显示赋值重载,会导致浅拷贝问题

  • 两对象的 _str 相同,都指向同一块空间,改变其中一个对象内容会相互影响
  • 两对象生命周期结束时,都会调用析构函数,对同一空间析构两次
    在这里插入图片描述
  • 若对本身赋值,直接返回本身
  • 开辟临时空间拷贝内容,避免new失败破坏原数据
  • 将原空间释放,同时 _str 指向拷贝好的临时空间
//赋值运算符重载
string& operator=(string& s)
{
	if (this != &s)
	{
		//_str的空间不一定为空,所以要用临时空间拷贝s._str的内容
		//再释放_str的原空间,同时_str指向开好的临时空间
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;

		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

迭代器

迭代器是类里内嵌的 typedef ,区分普通迭代器和 const 迭代器(标准库还有反向迭代器)
typedef char* iterator;
typedef const char* const_iterator;

  • 遍历可使用下标遍历、迭代器遍历、范围for
  • 范围for 本质上是迭代器,会自动识别普通迭代器和const迭代器
iterator begin() const
{
	return _str;
}
iterator end() const
{
	return _str + size();
}
//使用迭代器遍历
string::iterator it = s2.begin();
while (it != s2.end())
{
	cout << (*it) << " ";
	++it;
}
cout << endl;

容量相关

容量相关函数不会轻易缩容!!

reserve

更改容量

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		//拷贝后将原空间释放
		//cout << typeid(_str).name << endl;
		delete[] _str;
		_str = tmp;

		_capacity = n;
	}
}

resize

  • 若参数 n 小于 _size ,再 n 位置设置 ‘\0’ ,改变 _size 为 n,实现缩短串的长度
  • 若参数 n 大于 _size 小于 _capacity,将剩余空间设置为参数字符
  • 若参数 n 大于 _capacity,reserve扩容再设置字符
//resize
void resize(size_t n, char ch)
{
	if (n <= _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		if (n > _capacity)
			reserve(n);
		size_t i = _size;
		while (i < n)
		{
			_str[i] = ch;
			++i;
		}
		_size = n;
		_str[_size] = '\0';
	}
}

修改

push_back

  • 尾部插入字符
  • 无多余空间则进行扩容
void push_back(const char ch)
{
	if (_size == _capacity)
	{
		reserve(_capacity * 2);
	}
	_str[size()] = ch;
	++_size;
	_str[size()] = '\0';
}

append

  • 尾部插入字符串
  • 同样空间不够先扩容
  • 关于用strcpy而不用strcat
    strcat 追加内容前会先查找 ‘\0’ 的位置,会降低效率,使用strcpy可直接指定拷贝位置
void append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	//这里如果用strcat进行追加,
	//strcat会先查找'\0'的位置,会降低效率
	//所以这里使用strcpy直接指定拷贝位置
	strcpy(_str + _size, str);
}	

insert

  • 挪动数据时,应考虑0位置插入特殊情况
  • 区分插入字符与插入串进行重载
//insert区分字符与串进行重载
void insert(size_t pos, char ch)
{
	assert(pos <= _size);
	if (_size + 1 > _capacity)
	{
		reserve(_capacity + 1);
	}
	size_t end = _size + 1;
	//注意控制结束条件
	//考虑0位置插入情况
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_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(_size + len);
	}
	size_t end = _size + len;
	//注意各条件的控制
	while (end > pos + len - 1)
	{
		_str[end] = _str[end - len];
		--end;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

erase

关于npos

string::npos是一个静态成员常量,表示size_t的最大值(Maximum value for size_t)。该值表示“直到字符串结尾”,作为返回值它通常被用作表明没有匹配。
static const size_type npos = -1;
 
erase 函数参数为开始删除位置、删除长度

void erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);
	if (len != npos && len < _size - pos)
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
	else
	{
		_str[pos] = '\0';
		_size = pos;
	}
}

流运算符重载

流插入

//注意使用const和引用
ostream& operator<<(ostream& out, const string& s)
{
	for (auto e : s)
	{
		out << e;
	}
	return out;
}

流提取

  • 对于 ' ''\n'----
    C++认为是多个字符间的间隔,cin 和 scanf 提取时会忽略
    为了能将' ''\n'提取到缓冲区以便条件判断,使用get函数接收输入内容
  • 操作前先将原串 clear ----
    若不clear,对于"hello\0xxx\0"这样的串,扩容时的strcpy会以第一个'\0'结束,会造成不可预见的后果
  • 考虑插入内容较长:
    用一个buffer 串接受插入内容,buffer每满一次拷贝一次到待插入对象
    这样就减少了扩容次数
istream& operator>>(istream& in, string& s)
{
	s.clear();
	//get会将' '和'\n'都提取进缓冲区,方可进行while循环判断
	char ch = in.get();
	//char buffe	r[10];
	char buffer[128];

	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buffer[i++] = ch;
		if (i == 127)
		{
			//!!注意设置'\0'
			buffer[i] = '\0';
			s += buffer;
			i = 0;
		}
		ch = in.get();
	}
	if (i != 0)
	{
		//!!注意设置'\0'
		buffer[i] = '\0';
		s += buffer;
	}
	return in;
}

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

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

相关文章

自动驾驶—连续系统LQR最优控制的黎卡提方程推导

1. Why use the Riccati equation? 最优控制算法LQR是Linear Quadratic Regulator的缩写,Q、R就是需要设计的半正定矩阵和正定矩阵。考虑根据实车的情况去标定此参数,从理论和工程层面去理解,如果增大Q、减小R,则此时控制系统响应速度比较快速(比较剧烈),直观反映方向…

5月1日 9H45min|5.2 8H20min+30min|时间轴复盘

8:00 起床 8:00-8:30 洗漱吃饭 8:30-10:40 temporary pools阅读真题精读 (真的很慢了 不知道什么原因 感觉也没有彻底完全弄懂)【2h+10min】 10:40-11:10 午餐+酸奶(423+174KJ) 11:20-12:30 三篇阅读【1h+10min】 13:10-14:50 健身 14:50-15:45诵默写list…

Ae:画笔工具

画笔工具 Brush Tool 快捷键&#xff1a;Ctrl B 画笔工具 Brush Tool仅能工作在图层 Layer面板上。 双击纯色图层、像素图层等可打开图层面板。 在 Ae 中的每次画笔绘制都将新建一条路径&#xff0c;然后通过对路径的描边来显示绘制结果&#xff0c;故又称为“绘画描边”或“…

函数-实现交换两个变量的内容

用函数实现交换两个变量的内容&#xff0c;对于该问题我们该如何实现呢&#xff1f;在这里我就用整型变量来说明。 题目&#xff1a;写一个函数可以交换两个整形变量的内容。 我们先来看看如下代码&#xff1a; #include <stdio.h> void swap(int x, int y) {int tem…

Android进阶之光:Dagger2原理简要分析

Dagger2注入框架原理简要分析 使用Dagger2需要的依赖: implementation com.google.dagger:dagger-android:2.46 implementation com.google.dagger:dagger-android-support:2.46 annotationProcessor com.google.dagger:dagger-android-processor:2.46 annotationProcessor c…

第二十七章 碰撞体Collision(下)

本章节我们继续研究碰撞体&#xff0c;并且探索一下碰撞体与刚体之间的联系。我们回到之前的工程&#xff0c;然后给我们的紫色球体Sphere1也添加一个刚体组件。如下所示 此时&#xff0c;两个球体都具备了碰撞体和刚体组件。接下来&#xff0c;我们Play运行查看效果 我们发现&…

从零开始带你开发橙光游戏AVG框架(仿 葬花 )

来源 从零开始带你开发橙光游戏AVG框架【55课数 收费】 从零开始带你开发橙光游戏AVG框架 unity教程【16课数 免费】 。。。。。。 挺大的&#xff0c;因为很多音频&#xff0c;.git就有 2.6G AVG_20230413_2020.2.23f1c1 介绍 QuickSheet使用 bug 包报错 可能是我换了un…

LeetCode138. 复制带随机指针的链表

138. 复制带随机指针的链表 描述示例解题思路以及代码解法1解法2 描述 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成…

电脑文件加密软件哪个最好用:试试文件加密软件排行榜第一的EaseUS LockMyFile吧 | 军事级加密你值得拥有!!!

EaseUS LockMyFile是一款出色且安全可靠的军事级电脑文件加密管理软件&#xff0c;也叫易我文件加密软件&#xff0c;拥有文件隐藏、文件加锁、文件保护、读写监控、安全删除等诸多实用功能&#xff0c;能帮助大家锁定和隐藏闪存驱动器、外部USB 驱动器、内部硬盘驱动器以及局域…

51单片机(六)矩阵键盘和矩阵键盘密码锁

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

几种常见时间复杂度实例分析

多项式量级 常量阶 O(1) 对数阶 O(logn) 线性阶 O(n) 线性对数阶 O(nlogn) 平方阶O(n2 ),立方阶O(n3 )...k次方阶O(nk) 非多项式量级&#xff08;NP&#xff08;Non-Deterministic Polynomial&#xff0c;非确定多项式&#xff09;问题&#xff09; 指数阶O(2n) 阶乘阶…

离线数据同步Sqoop与DataX

文章目录 一、Sqoop安装与使用1、简介2、Sqoop安装3、Sqoop实例3.1 Mysql导入Hadoop3.2 Hadoop导出到Mysql 二、DataX概述与入门1、DataX概述1.1 简介1.2 框架设计1.3 运行原理 2、DataX与 Sqoop 的对比3、快速入门 三、DataX常用入门案例1、从stream 流读取数据并打印到控制台…

前端web3入门脚本六:套利夹子机器人,羊毛党必备

一、前言 DEX上有很多零风险套利的机会&#xff0c;包括三角套利&#xff0c;夹子机器人… 今天主要介绍一下架子机器人的思路和简易实现。 二、实现思路 套利原理&#xff1a; 夹子机器人的核心&#xff1a;在韭菜买入前以更低价格买入&#xff0c;并再韭菜买入后卖出&#…

Curator中的分布式锁解读

目录 基本介绍 基本配置 可重入锁InterProcessMutex 不可重入锁InterProcessSemaphoreMutex 可重入读写锁InterProcessReadWriteLock 联锁InterProcessMultiLock 信号量InterProcessSemaphoreV2 栅栏barrier 倒计数器 基本介绍 Curator是netflix公司开源的一套zookeeper…

C语言力扣简单题-无重复字符的最长子串

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 无重复字符的最长子串 题目&#xff1a; 代码思路&#xff1a; 代码表示&#xff1a; 无重复字符的最长子…

【C++】lambda表达式

文章目录 lambda表达式lambda概念lambda表达式的格式关于捕获列表常见问题: 使用lambda表达式交换两个数lambda表达式底层原理 lambda表达式 lambda概念 lambda表达式本质是一个匿名函数(因为它没有名字),恰当使用lambda表达式可以让代码变得简洁.并且可以提高代码的可读性 例…

ChatGPT实现HTML网页文本提取

网页自动化工具 既然ChatGPT对于编程语言有非常强大的理解能力&#xff0c;那么它是否可以用来自动化地处理网页呢&#xff1f;答案是肯定的。ChatGPT可以使用机器学习算法来识别网页元素中的文本&#xff0c;并抽取出有用的信息。 例如我们提供一段层数比较多的相对来说较为…

【五一创作】|【C++】AVL树的实现

文章目录 1.AVL树概念2. AVL树性质3.AVL树的实现insert插入情况分析更新平衡因子旋转处理左单旋右单旋在insert中判断左右单旋的条件双旋转左右双旋 整体代码 1.AVL树概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#…

PCIe数据链路层图文详细总结-PCIe专题知识(二)

目录 前言一、简介1.1 接收部件组成1.2 发送部件组成 二、数据链路层功能详细介绍2.1 DLLP介绍2.2 ACK/NAK协议2.3 发送端逻辑2.3.1 发送端TLP包处理总流程2.3.2 使用ACK/NAK协议详解 2.4 接收端逻辑2.4.1 接收端TLP包处理流程2.4.2 如何使用ACK/NAK协议 2.5 数据链路层发送报文…

Java每日一练(20230503)

1. 外观数列 给定一个正整数 n &#xff0c;输出外观数列的第 n 项。 「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定义的数字字符串序列&#xff1a; countAndSay(1) "1"c…