C++实现一个自定义字符串类(string)

本博客将详细介绍如何在C++中实现一个自定义的字符串类 string,这个类模仿了标准库中 std::string 的关键功能。这个过程将涵盖从声明到定义的每一步,重点介绍内存管理、操作符重载以及提供一些关键的实现细节。

首先:我们采用函数的声明与定义分离

目的是为了增加代码的可维护性以及提高效率:

1.在vs中,如果我们不实现声明与定义分离,那么编译器会默认认为你当前的函数是内联函数

:内联函数是一种编译器指令,它告诉编译器尝试在每个调用点展开函数体,而不是进行常规的函数调用。 

也就是说每次调用时会直接展开代码,这只适合一些代码量很小但调用机器频繁的函数。所以我们采用函数的声明与定义分离也可以进一步的优化效率。

2.函数声明与定义分离的注意事项和特点

  1. 改善编译依赖性:将函数声明放在头文件中,而将实现(定义)放在源文件中,可以减少编译时的依赖关系。当实现改变时,只需重新编译该源文件及其直接依赖,而不需要重新编译包含头文件的所有文件。

  2. 封装:通过隐藏实现细节,可以保护数据和实现,减少模块间的耦合。这符合封装的面向对象原则,有助于维护和扩展。

  3. 链接考虑:在多个源文件中分散定义的函数只有在最终链接时才会解析。这种分离确保了更好的模块化和错误隔离,尤其是在大型项目中。

  4. 避免多重定义:如果在多个源文件中包含相同的函数定义而未进行适当的静态或内联标记,将导致链接错误。正确的声明和定义分离有助于避免这种问题。

类声明和成员变量

首先,我们需要在 my_string 命名空间中定义字符串类的结构。这包括成员变量和函数的声明

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <assert.h>
#include <string.h>

namespace my_string {
	class string {
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
	public:
		const_iterator begin() const;
		const_iterator end() const;
		iterator begin();
		iterator end();
		const char* c_str() const;
		void earse(size_t pos, size_t len = npos);
		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void swap(string& s);
		void clear();
		void pushback(char ch);
		void append(const char* str);
		void reserve(size_t n);
		void resize(size_t n, char ch = '\0');
		size_t size()const;
		size_t capacity()const;
		size_t find(char ch, size_t pos = 0)const;
		size_t find(char* sub, size_t pos = 0)const;
		string(const char* str = "");
		string(const string& str);
		~string();
		string& operator=(string tmp);
		string& operator+=(const char* str);
		string& operator+=(char ch);
		bool empty()const;
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}
	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;
	public:
		static const int npos;
	};
	
	

在定义成员变量时我们采用缺省值来增加安全性。

经典的string类问题

浅拷贝

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

深拷贝

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

 也就是说我们如果不显示构造就会用默认的拷贝构造函数,就会出错!

 构造函数和析构函数实现

构造函数初始化字符串,并为字符数组分配内存。我们同时提供了拷贝构造函数和析构函数以处理资源管理:

string::string(const char* str )
	:_size(strlen(str))
{
	_capacity = _size ;
	_str = new char[_size + 1];
	strcpy(_str, str);
}



string::string(const string& str)
{
	string tmp(str._str);
	swap(tmp);
}



string::~string()
{
	delete[] _str;
	_str = nullptr;
	_size = 0;
	_capacity = 0;
}

动态内存管理实现

对于内存管理,我们实现了 reserveresize 方法来处理内存分配和调整大小的需求:

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::resize(size_t n, char ch) {
    if (n > _size) {
        reserve(n);
        std::fill(_str + _size, _str + n, ch);
        _size = n;
    }
    _str[_size] = '\0';
}

操作符重载

操作符重载使得字符串类的对象能以类似于原生数据类型的方式使用:

string& string::operator=(string tmp) {
    swap(tmp);
    return *this;
}

bool operator==(const string& s1, const string& s2) {
    return strcmp(s1.c_str(), s2.c_str()) == 0;
}

std::ostream& operator<<(std::ostream& out, const string& s) {
    out << s._str;
    return out;
}

 实现自定义迭代器

迭代器提供了几种操作,如递增 (++), 递减 (--), 解引用 (*), 等。在我们的例子中,由于使用了原始指针作为迭代器,这些操作已经由指针的自然行为直接支持


		//这里我们手搓一个迭代器
		typedef char* iterator;
		typedef const char* const_iterator;
		const_iterator begin() const
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

 支持范围 for 循环

在C++定义中,范围for的底层实现是迭代器,所以哪怕我们要自定义使用范围for

我们就必须手动实现一个一模一样的迭代器这样就可以使用了

实现流插入和输出操作

在自定义实现流插入时,我们发现只能从右边传递给左边,但左边默认是*this,此时如果还是右边传递给左边,那不就成立 cout 传递给 *this 了,道反天罡!所以我们采取定义全局函数,将参数变成两个自定义的就可以解决,由于cout 是ostream流里的  cin是istream流里的所以返回类型也是对应的流类型

std::ostream& operator<<(std::ostream& out, const string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}

std::istream& operator>>(std::istream& in, string s)
{
	s.clear();
	char ch;
	//in >> ch;  //不能用下标 还没开孔加呢
	// 
	ch = in.get();
	char buff[128];
	size_t i = 0;

	//cin 和 scanf读不到空格
	//C语言用getchar 
	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++中从头到尾实现一个自定义的 string 类。我们不仅关注了如何实现基础功能,还涉及了如何通过操作符重载提高类的可用性,以及如何确保类在处理动态内存时的安全和效率。通过这种方式,我们能更深入地理解标准 std::string 类的内部工作机制,并能在需要时为自己的应用定制特定的行为。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ArcGIS Pro 3D建模简明教程

在本文中&#xff0c;我讲述了我最近一直在探索的在 ArcGIS Pro 中设计 3D 模型的过程。 我的目标是尽可能避免与其他软件交互&#xff08;即使是专门用于 3D 建模的软件&#xff09;&#xff0c;并利用 Pro 可以提供的可能性。 这个短暂的旅程分为三个不同的阶段&#xff1a;…

【SGDR】《SGDR:Stochastic Gradient Descent with Warm Restarts》

arXiv-2016 code: https://github.com/loshchil/SGDR/blob/master/SGDR_WRNs.py 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method5 Experiments5.1 Datasets and Metric5.2 Single-Model Results5.3 Ensemble Results5.4 Experiment…

kali工具----枚举工具

一、枚举工具 枚举是一类程序&#xff0c;它允许用户从一个网络中收集某一类的所有相关信息。本节将介绍DNS枚举和SNMP枚举技术。DNS枚举可以收集本地所有DNS服务和相关条目。DNS枚举可以帮助用户收集目标组织的关键信息&#xff0c;如用户名、计算机名和IP地址等&#xff0c;…

HarmonyOS实战开发-视频播放、如何实现了视频播放、暂停、调节倍速、切换视频的功能。

介绍 视频播放的主要工作是将视频数据转码并输出到设备进行播放&#xff0c;同时管理播放任务。本文将对视频播放全流程、视频切换、视频循环播放等场景开发进行介绍说明。 本示例主要展示了播放本地视频和网络视频相关功能,使用 ohos.multimedia.media,ohos.resourceManager,…

Python 全栈系列239 使用消息队列完成分布式任务

说明 在Python - 深度学习系列32 - glm2接口部署实践提到&#xff0c;通过部署本地化大模型来完成特定的任务。 由于大模型的部署依赖显卡&#xff0c;且常规量级的任务需要大量的worker支持&#xff0c;从成本考虑&#xff0c;租用算力机是比较经济的。由于任务是属于超高计…

【opencv】示例-inpaint.cpp 图像修复是通过填充损坏图像部分从而修复这些损坏的过程...

原始图像 这段代码展示了一个使用OpenCV库进行图像修复的例子。它首先包含了处理图像编码、解码、显示、处理和照片处理所必要的OpenCV模块的头文件。然后利用cv和std命名空间下的类和方法。通过定义一个鼠标回调函数onMouse来处理图像上的绘图操作&#xff0c;并通过主函数mai…

React添加到现有项目

1.检查现有项目的根目录下是否有package.json文件 如果没有&#xff0c;则在项目的根目录下初始化一个package.json配置文件 2.在根目录下安装react和react-dom依赖 npm install --save react react-dom react-scripts安装成功后&#xff0c;react、react-dom以及react-scr…

汽车制造业PMC组态应用最佳实践

01案例及行业介绍 汽车制造工业是我国国民经济的重要支柱产业&#xff0c;汽车制造工厂一般包含冲压、焊装、涂装、总装四大车间。每辆汽车的生产过程被分解成很多加工任务下发给各个车间进行完成。车辆从冲压车间开始到总装车间结束一直进行不同类型的工序加工。 PMC即生产控…

Sarson Funds 在 Casper 测试网推出稳定币 csprUSD

Sarson Funds 与 Casper Association 合作&#xff0c;在 Casper Network &#xff08;CSPR&#xff09;测试网上推出了 csprUSD 稳定币。 作为最新的法币背书型稳定币&#xff0c;csprUSD 进入了数字货币市场&#xff0c;与 Ripple 和 Cardano 等组织近期推出的产品定位一致。…

使用vite从头搭建一个vue3项目(二)创建目录文件夹以及添加vue-router

目录 一、创建 vue3 项目 vite-vue3-project-js二、创建项目目录三、创建Home、About组件以及 vue-router 配置路由四、修改完成后页面 一、创建 vue3 项目 vite-vue3-project-js 使用 vite 创建一个极简 vue3 项目请参考此文章&#xff1a;使用Vite创建一个vue3项目 下面是我…

基于GitHub的开源讨论系统,赋予网站交互可能

Giscus&#xff1a;让每一条见解直达GitHub&#xff0c;用Giscus开启网站与社区的无缝对话新纪元&#xff01;- 精选真开源&#xff0c;释放新价值。 概览 纯静态网站或博客&#xff0c;由于没有数据存储功能&#xff0c;经常借助第三方的评论系统以插件的方式集成进来&#x…

Java 自定義 List<T> 分頁工具

Java 自定義 List 分頁工具 PS: T可修改为对应的实体 rt com.google.common.collect.Lists;import java.util.Arrays; import java.util.Collections; import java.util.List;/*** ClassName: MyPageHelper* Descripution: List<T>分頁工具**/ public class MyPageHelp…

elementUI 下拉框加提示文案

效果如下&#xff1a; 展示文案在最下面&#xff0c;跟选项有个分割线 <el-select v-model"value" placeholder"请选择" clearable popper-class"addNotice" class"addNoticeS" visible-change"(v) >selectNotice(v,展示…

使用clickhouse-backup备份和恢复数据

作者&#xff1a;俊达 介绍 clickhouse-backup是altinity提供的一个clickhouse数据库备份和恢复的工具&#xff0c;开源项目地址&#xff1a;https://github.com/Altinity/clickhouse-backup 功能上能满足日常数据库备份恢复的需求&#xff1a; 支持单表/全库备份支持备份上…

Day23_学点儿IDEA_单元测试@Test在新module项目中失效、Jackson核心对象 ObjectMapper识别不到

版本 IntelliJ IDEA 2023.2.4 目录结构 Study(Project) ├──JavaSE(Module) │ └──xxxx └──JavaWeb(Module)└──xxxx问题 1.1 在JavaSE项目中正常可以用的单元测试Test&#xff0c;到了JavaWeb项目中不起作用了 1.2 解决方法 如果是新创建的项目&#xff0c;先…

时间序列分析 # 平稳性检验和ARMA模型的识别与定阶 #R语言

掌握单位根检验的原理并能解读结果&#xff1b;掌握利用序列的自相关图和偏自相关图识别模型并进行初步定阶。 原始数据在文末&#xff01;&#xff01;&#xff01; 练习1、根据某1971年9月-1993年6月澳大利亚季度常住人口变动&#xff08;单位&#xff1a;千人&#xff09;的…

Arthas排查工具

简介 | arthas (aliyun.com) 在线安装 #下载jar包 curl -O https://arthas.aliyun.com/arthas-boot.jar#启动会先检测虚拟机进程&#xff0c;如果没有启动失败(idea) java -jar arthas-boot.jar linux安装与window一样 卸载arthas rm -rf ~/.arthas/ rm -rf ~/logs/arthas

时钟周期检测标志信号

在某些情况下需要对系统时钟分频后的时钟进行周期检测&#xff0c;引出周期标志信号以便在后续其他情况的使用。虽然在大多数情况下我们能够知道分频后的时钟是系统时钟的几倍分频&#xff0c;但为增强在分频时钟改变情况下周期标志信号的复用性或对未知时钟的周期检测&#xf…

C++格式化输出开源库fmt入手教程

fmt项目快速上手指南 1. cmake环境配置 include(FetchContent) FetchContent_Declare(fmtGIT_REPOSITORY https://github.com/fmtlib/fmtGIT_TAG 10.0.0GIT_SHALLOW TRUE) # 1. 下载fmt库 FetchContent_MakeAvailable(fmt)add_executable(fmt_guide main.cpp) # 2. 链接fmt库…

Git基础操作及其分支管理

目录 一、git的用处&#xff1f; 1.1 git也不是银弹 二、安装git 三、git基础操作 3.1 创建git本地仓库 3.2 配置Git 3.3 认识工作区、暂存区、版本库 3.4 添加文件 3.5 Git文件目录 3.6 版本回退 3.7 撤销修改 3.7.1 对于工作区的代码&#xff0c;还没有进行add操作…