C++ Learning string类模拟实现

news2025/12/14 11:56:11

string类模拟实现

std::string 类作为 C++ 标准库中非常重要的一个类型,它封装了字符串的动态分配、内存管理以及其他字符串操作。

基本构思与设计

一个简化版的 string 类需要满足以下基本功能:

  • 存储一个字符数组(char*)。
  • 记录字符串的长度和容量。
  • 实现一些常见的字符串操作:如拷贝构造、赋值、拼接、访问字符等。
  • 动态扩展数组以支持变长字符串。

基础成员变量和构造函数

首先,我们定义一个 String 类的基本结构。为了模拟 std::string,我们需要存储字符数组、字符串的大小以及容量。

class String
{
public:
    // ...

private:
	char* data;
	size_t size;
	size_t capacity;
};

其中data是字符数组,size表示已存储字符个数,capacity表示能存储字符个数(不包括"\0"

// 默认构造函数
String()
	:data(new char[1])
{
	data[0] = '\0';
	size = capacity = 0;
}

// 构造函数:根据 C 字符串初始化
String(const char* str)
{
	size = strlen(str);
	capacity = size;
	data = new char[capacity + 1]; // 多一个空间用于存放'\0'
	strcpy(data, str);
}

两个构造函数还可以合二为一采用缺省值写法。

String::String(const char* str = "")
{
	size = strlen(str);
	capacity = size;
	data = new char[capacity + 1];
	strcpy(data, str);
}

特别注意:此处的缺省值只能给str = ""表示一个空串。若给其他值会导致在构造空串时不合理。

拷贝构造函数和赋值

// 拷贝构造函数
String(const String& rhs)
{
	size = rhs.size;
	capacity = rhs.capacity;
	data = new char[capacity + 1];
	strcpy(data, rhs.data);
}

// 赋值操作符重载
String& operator=(const String& rhs)
{
	// 检查自赋值    
	if (this != &rhs)
	{
		delete[] data; // 释放原有的内存资源
		size = rhs.size;
		capacity = rhs.capacity;
		data = new char[capacity + 1];
		strcpy(data, rhs.data);
	}
	return *this;
}

特别注意:赋值操作符重载时不要忘记释放原有的内存资源

reserve函数

// 重新分配内存
void String::reserve(size_t new_capacity)
{
	// 如果新的容量小于当前容量,不做任何事情
	if (new_capacity > capacity) {
		char* new_data = new char[new_capacity + 1];
		strcpy(new_data, data);
		delete[] data;// 释放原有的内存资源
		data = new_data;
		capacity = new_capacity;
	}
}

字符串拼接相关函数

// 追加字符
void push_back(char ch)
{
	if (size == capacity)
	{
		// 重新分配内存
		// 先判断capacity是否为0,如果为0,新的容量为2,否则为原来的2倍
		size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
		reserve(new_capacity);
	}
	data[size++] = ch;
	data[size] = '\0'; // 添加字符串结束符
}

// 追加字符串
void append(const char* str)
{
	size_t new_size = size + strlen(str);
	if (new_size > capacity)
	{
		// 重新分配内存
		size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
		// 扩容到足够容纳新字符串的大小,以2倍数扩容
		while (new_capacity < new_size)
		{
			new_capacity *= 2;
		}
		reserve(new_capacity);
	}
	strcat(data, str);
	size = new_size;
}
重载+=运算符
// 重载+=运算符
String& String::operator+=(char ch)
{
	push_back(ch);
	return *this;
}
String& String::operator+=(const char* str)
{
	append(str);
	return *this;
}

直接复用上面的代码

insert函数
String& insert(size_t pos, char ch)
{
	assert(pos <= size);
	if (size == capacity)
	{
		// 重新分配内存
		size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
		reserve(new_capacity);
	}

	// 将pos位置及其后面的字符向后移动一个位置
	for (size_t i = size; i > pos; --i)
	{
		data[i] = data[i - 1];
	}

	data[pos] = ch;
	++size;
	return *this;
}

String& insert(size_t pos, const char* str)
{
	assert(pos <= size);
	size_t len = strlen(str);
	size_t new_size = size + len;
	if (new_size > capacity)
	{
		// 重新分配内存
		size_t new_capacity = capacity == 0 ? 2 : 2 * capacity;
		// 扩容到足够容纳新字符串的大小,以2倍数扩容
		while (new_capacity < new_size)
		{
			new_capacity *= 2;
		}
		reserve(new_capacity);
	}

	// 将pos位置及其后面的字符向后移动len个位置
	for (size_t i = size; i >= pos; --i)
	{
		data[i + len] = data[i];
	}

	// 将str插入到pos位置
	for (size_t i = 0; i < len; ++i)
	{
		data[pos + i] = str[i];
	}
	size = new_size;
	return *this;
}

此处需要注意挪动数据的方式。

erase函数

// 删除函数
String& erase(size_t pos, size_t len = npos)
{
	assert(pos < size);
	// 如果len超过pos到字符串末尾的长度,只删除pos到字符串末尾的长度
	if (len > size - pos) {
		data[pos] = '\0';
		size = pos;
	}
	else {
		// 将pos+len位置及其后面的字符向前移动len个位置
		size_t i = pos + len;
		while (i <= size)
		{
			data[pos++] = data[i++];
		}
		size -= len;
	}
	return *this;
}

其中npos是一个常量

const static size_t npos = -1;

resize函数

// 调整字符串大小
void resize(size_t new_size, char ch = '\0')
{
	if (new_size > size)
	{
		if (new_size > capacity)
		{
			// 重新分配内存
			reserve(new_size);
		}

		// 将ch添加到字符串末尾
		for (size_t i = size; i < new_size; ++i)
		{
			data[i] = ch;
		}
	}
	data[new_size] = '\0'; // 添加字符串结束符
	size = new_size;
}

查找函数

size_t find(char ch, size_t pos) const
{
	for (size_t i = pos; i < size; ++i) {
		if (data[i] == ch) {
			return i;
		}
	}
	return npos;
}

size_t find(const char* str, size_t pos) const
{
	char* p = strstr(data + pos, str);
	return p ? p - data : npos;
}

输入输出

istream& operator>>(istream& is, String& str)
{
	char ch;
	ch = is.get();
	while (ch != ' ' && ch != '\n') {
		str.push_back(ch);
		ch = is.get();// 读取下一个字符,可以读取空格
	}
	return is;
}

istream& getline(istream& is, String& str)
{
	char ch;
	ch = is.get();
	while (ch != '\n') {
		str.push_back(ch);
		ch = is.get();// 读取下一个字符,可以读取空格
	}
	return is;
}

ostream& operator<<(ostream& os, const String& str)
{
	os << str.c_str();
	return os;
}

比较运算符重载

bool operator==(const String& lhs, const String& rhs)
{
	int ret = strcmp(lhs.c_str(), rhs.c_str());
	return ret == 0;
}

bool operator!=(const String& lhs, const String& rhs)
{
	return !(lhs == rhs);
}

bool operator<(const String& lhs, const String& rhs)
{
	int ret = strcmp(lhs.c_str(), rhs.c_str());
	return ret < 0;
}

bool operator>(const String& lhs, const String& rhs)
{
	int ret = strcmp(lhs.c_str(), rhs.c_str());
	return ret > 0;
}

bool operator<=(const String& lhs, const String& rhs)
{
	return !(lhs > rhs);
}

bool operator>=(const String& lhs, const String& rhs)
{
	return !(lhs < rhs);
}

重载[]运算符

char& operator[](size_t i)
{
	return data[i];
}

const char& operator[](size_t i) const
{
	return data[i];
}

迭代器

iterator begin()
{
	return data;
}

const_iterator begin() const
{
	return data;
}

iterator end()
{
	return data + size;
}

const_iterator end() const
{
	return data + size;
}

其中iterator来自typedef

typedef char* iterator;
typedef const char* const_iterator;

大小相关

size_t Size() const
{
	return size;
}

size_t length() const
{
	return size;
}

size_t Capacity() const
{
	return capacity;
}

完整代码

GitHub链接

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

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

相关文章

AI炼丹日志-27 - Anubis 通过 PoW工作量证明的反爬虫组件 上手指南 原理解析

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇&#xff1a; MyBatis 更新完毕目前开始更新 Spring&#xff0c;一起深入浅出&#xff01; 大数据篇 300&#xff1a; Hadoop&…

阿姆达尔定律的演进:古斯塔夫森定律

前言 在上一篇文章《使用阿姆达尔定律来提升效率》中提到的阿姆达尔定律前提是假设问题的规模保持不变&#xff0c;并且给定一台速度更快的机器&#xff0c;目标是更快地解决问题。然而&#xff0c;在大多数情况下&#xff0c;这并不完全正确。当有一台更快的机器时&#xff0…

JavaScript极致性能优化全攻略

JavaScript性能优化深度指南 1 引言 JavaScript性能优化在现代Web开发中至关重要。随着Web应用日益复杂,性能直接影响用户体验、搜索引擎排名和业务转化率。研究表明,页面加载时间每增加1秒,转化率下降7%,跳出率增加32%。通过优化JavaScript性能,开发者可以: 提升用户满…

Transformer核心原理

简介 在人工智能技术飞速发展的今天&#xff0c;Transformer模型凭借其强大的序列处理能力和自注意力机制&#xff0c;成为自然语言处理、计算机视觉、语音识别等领域的核心技术。本文将从基础理论出发&#xff0c;结合企业级开发实践&#xff0c;深入解析Transformer模型的原…

Grafana-State timeline状态时间线

显示随时间推移的状态变化 状态区域&#xff1a;即状态时间线上的状态显示的条或带&#xff0c;区域长度表示状态持续时间或频率 数据格式要求&#xff08;可视化效果最佳&#xff09;&#xff1a; 时间戳实体名称&#xff08;即&#xff1a;正在监控的目标对应名称&#xf…

解决CSDN等网站访问不了的问题

原文网址&#xff1a;解决CSDN等网站访问不了的问题-CSDN博客 简介 本文介绍解决CSDN等网站访问不了的方法。 问题描述 CSDN访问不了了&#xff0c;页面是空的。 问题解决 方案1&#xff1a;修改DNS 可能是dns的问题&#xff0c;需要重新配置。 国内常用的dns是&#x…

C++ Vector算法精讲与底层探秘:从经典例题到性能优化全解析

前引&#xff1a;在C标准模板库&#xff08;STL&#xff09;中&#xff0c;vector作为动态数组的实现&#xff0c;既是算法题解的基石&#xff0c;也是性能优化的关键战场。其连续内存布局、动态扩容机制和丰富的成员函数&#xff0c;使其在面试高频题&#xff08;如LeetCode、…

Flowith,有一种Agent叫无限

大家好&#xff0c;我是羊仔&#xff0c;专注AI工具、智能体、编程。 今天羊仔要和大家聊聊一个最近发现的超级实用的Agent平台&#xff0c;名字叫Flowith。 这篇文章会带你从零了解到实战体验&#xff0c;搞清楚Flowith是如何让工作效率飙升好几倍&#xff0c;甚至重新定义未…

系统思考:短期利益与长期系统影响

一个决策难题&#xff1a;一家公司接到了一个大订单&#xff0c;客户提出了10%的降价要求&#xff0c;而企业的产能还无法满足客户的需求。你会选择增加产能&#xff0c;接受这个订单&#xff0c;还是拒绝&#xff1f;从系统思考的角度来看&#xff0c;这个决策不仅仅是一个简单…

HTTP连接管理——短连接,长连接,HTTP 流水线

连接管理是一个 HTTP 的关键话题&#xff1a;打开和保持连接在很大程度上影响着网站和 Web 应用程序的性能。在 HTTP/1.x 里有多种模型&#xff1a;短连接、_长连接_和 HTTP 流水线。 下面分别来详细解释 短连接 HTTP 协议最初&#xff08;0.9/1.0&#xff09;是个非常简单的…

【免费】2004-2020年各省电力消费量数据

2004-2020年各省电力消费量数据 1、时间&#xff1a;2004-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、电力消费量(亿千瓦小时) 4、范围&#xff1a;31省 5、指标说明&#xff1a;电力消费量是指在一定时期内&#xff…

登录的写法,routerHook具体配置,流程

routerHook挂在在index.js/main.js下的&#xff0c;找不到可以去那边看一下 vuex需要做的&#xff1a; //创建token的sate&#xff0c;从本地取 let token window.localStorage.getItem(token) // 存储用户登录信息let currentUserInfo reactive({userinfo: {}}) //存根据不…

工作服/反光衣检测算法AI智能分析网关V4安全作业风险预警方案:筑牢矿山/工地/工厂等多场景安全防线

一、方案背景​ 在工地、矿山、工厂等高危作业场景&#xff0c;反光衣是保障人员安全的必备装备。但传统人工巡查存在效率低、易疏漏等问题&#xff0c;难以实现实时监管。AI智能分析网关V4基于人工智能技术&#xff0c;可自动识别人员着装状态&#xff0c;精准定位未穿反光衣…

设计模式——中介者设计模式(行为型)

摘要 文章详细介绍了中介者设计模式&#xff0c;这是一种行为型设计模式&#xff0c;通过中介者对象封装多个对象间的交互&#xff0c;降低系统耦合度。文中阐述了其核心角色、优缺点、适用场景&#xff0c;并通过类图、时序图、实现方式、实战示例等多方面进行讲解&#xff0…

MinGW-w64的安装详细步骤(c_c++的编译器gcc、g++的windows版,win10、win11真实可用)

文章目录 1、MinGW的定义2、MinGW的主要组件3、MinGW-w64下载与安装 3.1、下载解压安装地址3.2、MinGW-w64环境变量的设置 4、验证MinGW是否安装成功5、编写一段简单的代码验证下6、总结 1、MinGW的定义 MinGW&#xff08;Minimalist GNU for Windows&#xff09; 是一个用…

LabVIEW磁悬浮轴承传感器故障识别

针对工业高端装备中主动磁悬浮轴承&#xff08;AMB&#xff09;的位移传感器故障检测需求&#xff0c;基于 LabVIEW 平台构建了一套高精度故障识别系统。通过集成品牌硬件与 LabVIEW 的信号处理能力&#xff0c;实现了传感器探头故障的实时监测与精准定位&#xff0c;解决了传统…

多线程1(Thread)

认识线程&#xff08;Thread&#xff09; 在进程中&#xff0c;要创建一个进程和销毁一个进程所消耗的硬件和软件资源是巨大的&#xff0c;因此为了优化上述过程&#xff0c;我们引入了“线程”。 线程是系统调度的基本单位。 1&#xff09;线程和进程的关系 可以认为进程包…

NVIDIA DOCA 3.0:引领AI基础设施革命的引擎简析

引言 在当今快速发展的AI时代,大规模AI模型的训练和部署对数据中心基础设施提出了前所未有的挑战。传统的CPU-centric架构已经难以满足超大规模AI工作负载对性能、效率和安全性的需求。NVIDIA于2025年4月正式发布了DOCA 3.0软件框架,这一创新性平台彻底改变了AI基础设施的设计…

小家电外贸出口新利器:WD8001低成本风扇智能控制方案全解析

低成本单节电池风扇解决方案WD8001 用途 低成本单节电池风扇解决方案WD8001用于小功率风扇供电及控制&#xff0c;具有三个档位调节、自动停机及锁机功能。 基本参数 充电参数&#xff1a;输入5V/500mA&#xff0c;满电4.2V&#xff0c;充电指示灯亮&#xff0c;满电后熄灭…

C++实现汉诺塔游戏用户交互

目录 一、模型调整(一)模型定义(二)模型实现1.电脑自动完成部分2.SDL图形显示2.1拿起放下盘子的函数2.2左右移动手指的函数 二、处理用户输入&#xff0c;进行人机分流三、总结四、源码下载 上篇文章使用C语言实现汉诺塔游戏电脑自动完成的步骤&#xff0c;还没有实现用户交互&…