string底层实现细节

devtools/2025/1/21 13:02:37/

一、部分代码展示

#pragma once
#include<cstring>
#include<cassert>
#include<iostream>
using namespace std;
namespace bit
{class string{public:// 迭代器类指针// 范围for就是在编译时替换成迭代器遍历,*it返回给chtypedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}/*string():_str(new char[1]),_size(0),_capacity(0){_str[0] = '\0';}*/string(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}// 拷贝构造要深拷贝,防止指针指向同一个空间/*string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}*/// 现代写法string(const string& s){string tmp(s._str);swap(tmp);}/*string& operator=(const string& s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}*//*string& operator=(const string& s){string ss(s);swap(ss);return *this;}*/// 现代写法// 去掉引用就是传值传参,s是拷贝,修改不会改变外面的值string& operator=(string s){swap(s);return *this;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}const char* c_str() const{return _str;}char& operator[](size_t pos){// 传统的数组,写是越界抽查,读不会检查出assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{// 传统的数组,写是越界抽查,读不会检查出assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n];strcpy(tmp, _str);delete[] tmp;_str = tmp;_capacity = n;}}void push_back(char ch){/*if (_size == _capacity){reserve(capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';*/insert(_size, ch);}void append(const char* str){//size_t len = strlen(str);//if (_size + len > _capacity)//{//	reserve(_size + len);//}strcpy会把str的\0也拷过去//strcpy(_str + _size, str);//_size += len;insert(_size, str);}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}void insert(size_t pos, char ch){assert(pos <= _size); // 等于就是尾插if (_size == _capacity){reserve(capacity == 0 ? 4 : 2 * _capacity);}//int end = _size; // 防止头插size_t减到负数变特别大的正数//while (end >= (int)pos)//{//	_str[end + 1] = _str[end];//	end--;//}int end = _size + 1;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);}int end = _size + len;while (end - len >= pos){_str[end] = _str[end - len];end--;}strcpy(_str + _size, str);_size += len;}void erase(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || len >= _size - pos){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}void resize(size_t n, char c = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i < n; i++)_str[i] = c;_str[n] = '\0';_size = n;}}/* 库里面的swap是三次拷贝一次析构,消耗大编译器找函数的原则:1.编译器指挥向上找2.先在局部找,再在全局找,也可以在命名空间找3.指定了命名空间也在命名空间找string库里面也实现了两个参数的全局swap,目的就是为了区分算法库swap有现成用现成,所以有3个swap也是调全局swap*/void swap(string& str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}size_t find(char ch, size_t pos = 0) const{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;}size_t find(const char* sub, size_t pos = 0) const{assert(pos < _size);const char* p = strstr(_str + pos, sub);if (p)return p - _str;elsereturn pos;}string substr(size_t pos = 0, size_t len = npos){string sub;if (len == npos || len >= _size - pos)for (size_t i = pos; i < _size; i++)sub += _str[i];elsefor (size_t i = pos; i < pos + len; i++)sub += _str[i];return sub;}void clear(){_size = 0;_str[_size] = '\0';}~string(){delete[] _str;_size = 0;_capacity = 0;}private:char* _str;size_t _capacity;size_t _size;public:static const int npos;};const int string::npos = -1;void swap(string& x, string& y){x.swap(y);}// 重载比较函数可以是全局的,这样第一个参数就可以不是string,更灵活bool operator==(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;}bool operator<(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}// 流提取的重载必须全局,但不一定友元// 友元的目的就是为了获取类的私有成员变量进行打印ostream& operator<<(ostream& out, const string& s){for (auto ch : s)out << ch;return out;}//istream& operator>>(istream& in, string& s)//{//	s.clear();//	char ch;//	// in >> ch; // istream默认不会读空格和换行符//	ch = in.get(); // 相当于c语言里面的getchar()//	while (ch != ' ' && ch != '\n')//	{//		s += ch;//		ch = in.get();//	}//	return in;//}// 优化:缓冲区istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();char buf[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}ch == in.get();}if (i != 0){buf[i] = '\0';s += buf;}return in;}//istream& getline(istream& in, string& s)//{//	s.clear();//	char ch;//	// in >> ch; // istream默认不会读空格和换行符//	ch = in.get(); // 相当于c语言里面的getchar()//	while (ch != '\n')//	{//		s += ch;//		ch = in.get();//	}//	return in;//}// 优化istream& getline(istream& in, string& s){s.clear();char ch;ch = in.get();char buf[128];size_t i = 0;while (ch != '\n'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}ch == in.get();}if (i != 0){buf[i] = '\0';s += buf;}return in;}
}

二、细节

1、成员变量

要存储一个 string 对象需要有起始地址 _str,string 对象大小 _size,string 对象容量 _capacity

2、构造函数

(1)构造函数 string(const char* str = "") 

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

这个构造函数是深拷贝开空间

(2)拷贝构造 string(const string& s) 

/*string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}*/
// 现代写法
string(const string& s)
{string tmp(s._str);swap(tmp);
}

拷贝构造的参数是不能改变的,所以用一个局部变量记录 s 信息再交换,最后除了函数 tmp 带着 *this 的信息销毁。一定要深拷贝,不然多个指针指向同一个空间,析构多次报错。

3、重载 = 运算符 string& operator=(string& s)

/*string& operator=(const string& s)
{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;
}*/
/*string& operator=(const string& s)
{string ss(s);swap(ss);return *this;
}*/
// 现代写法
// 去掉引用就是传值传参,s是拷贝,修改不会改变外面的值
string& operator=(string s)
{swap(s);return *this;
}

三种写法

第一种,自己深拷贝

第二种,在函数内部用拷贝构造 + 交换

第三种,去掉 const 和 & 在传参的过程中拷贝构造临时变量 s,函数内部直接交换

4、交换函数 void swap(string& str)

void swap(string& str)
{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}

用于外部交换两个 string 对象,也用于内部构造对象

 库里面的swap是三次拷贝一次析构,消耗大
 编译器找函数的原则:
 1.编译器指挥向上找
 2.先在局部找,再在全局找,也可以在命名空间找
 3.指定了命名空间也在命名空间找
 string库里面也实现了两个参数的全局swap,目的就是为了区分算法库swap
 有现成用现成,所以有3个swap也是调全局swap

5、迭代器 iterator

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

string 的迭代器可以用指针

6、插入函数 void insert(size_t pos, char ch)

void insert(size_t pos, char ch)
{assert(pos <= _size); // 等于就是尾插if (_size == _capacity){reserve(capacity == 0 ? 4 : 2 * _capacity);}//int end = _size; // 防止头插size_t减到负数变特别大的正数//while (end >= (int)pos)//{//	_str[end + 1] = _str[end];//	end--;//}int end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;
}

注意 size_t 类型减到负数会变成很大的值

7、比较运算符的重载

bool operator==(const string& s1, const string& s2)
{int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;
}bool operator<(const string& s1, const string& s2)
{int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;
}bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}

重载比较函数可以是全局的,这样第一个参数就可以不是string,更灵活

8、流提取 ostream& operator<<(ostream& out, const string& s)

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

流提取的重载必须全局,但不一定友元
友元的目的就是为了获取类的私有成员变量进行打印

9、流插入 istream& operator>>(istream& in, string& s)

//istream& operator>>(istream& in, string& s)
//{
//	s.clear();
//	char ch;
//	// in >> ch; // istream默认不会读空格和换行符
//	ch = in.get(); // 相当于c语言里面的getchar()
//	while (ch != ' ' && ch != '\n')
//	{
//		s += ch;
//		ch = in.get();
//	}
//	return in;
//}// 优化:缓冲区
istream& operator>>(istream& in, string& s)
{s.clear();char ch;ch = in.get();char buf[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}ch == in.get();}if (i != 0){buf[i] = '\0';s += buf;}return in;
}

istream默认不会读空格和换行符,所以用 istream 的函数 get() 来获取一个字符,包括空格换行


http://www.ppmy.cn/devtools/152354.html

相关文章

B站评论系统的多级存储架构

以下文章来源于哔哩哔哩技术 &#xff0c;作者业务 哔哩哔哩技术. 提供B站相关技术的介绍和讲解 1. 背景 评论是 B站生态的重要组成部分&#xff0c;涵盖了 UP 主与用户的互动、平台内容的推荐与优化、社区文化建设以及用户情感满足。B站的评论区不仅是用户互动的核心场所&…

Unity2021.3.13崩溃的一种情况

如果出现如下的报错&#xff0c;可能是软件冲突的原因。自己的原因是使用f.lux这款软件似乎和Unity相互冲突&#xff0c;出现下面报错。 错误信息如上图

单例及线程池的实现及感悟分享

碎碎念&#xff1a; 有快3个月没有写博客了&#xff0c;这段时间很多事&#xff0c;抽不出空来写博客。后面的博客可能风格也会转变&#xff0c;从理论到实战为主&#xff0c;尽量减少源码的解读。(看完源码后面自己也忘了QAQ&#xff09;&#xff0c;本篇博客主要介绍线程池的…

TinyEngine v2.1版本发布:全新的区块方案和画布通信方案,打造更强力的可拓展低代码引擎

前言 2025年蛇年已经到来&#xff0c;TinyEngine v2.1.0 版本也已经蛇气腾腾的发布了出来&#xff0c;新年新气象&#xff0c;为了让大家更详细了解到 v2.1.0 的内容更新&#xff0c;我们特此列举了该版本中的一些重要特性更新。 v2.1.0变更特性概览 1、使用了新的纯前端区块…

MIAOYUN信创云原生项目亮相西部“中试”生态对接活动

近日&#xff0c;以“构建‘中试’生态&#xff0c;赋能科技成果转化”为主题的“科创天府智汇蓉城”西部“中试”生态对接活动在成都高新区菁蓉汇隆重开幕。活动分为成果展览、“中试”生态主场以及成果路演洽谈对接三大板块。在成果展览环节&#xff0c;成都元来云志科技有限…

OpenHarmony API 设计规范

OpenHarmony API 设计规范 修订记录 版本作者时间更新内容v0.1&#xff0c;试运行版OpenHarmony API SIG2022年11月初版发布 目的 API是软件实现者提供给使用者在编程界面上的定义&#xff0c;API在很大程度上体现了软件实体的能力范围。 同时&#xff0c;API定义的好坏极…

《鸿蒙 HarmonyOS 应用开发从入门到精通(第 2 版)》学习笔记 ——HarmonyOS 环境搭建之安装DevEco Studio

作为一款开发工具&#xff0c;除了具有基本的代码开发、编译构建及调测等功能外&#xff0c;DevEco Studio还具有如下特点&#xff1a; 高效智能代码编辑&#xff1a;支持Java、XML、ArkTS、JS、C/C等语言的代码高亮、代码智能补齐、代码错误检查、代码自动跳转、代码格式化、…

[Unity]【游戏开发】 脚本创建物体的实践与技巧

在Unity游戏开发中,动态创建物体是一个常见的需求。为了提高开发效率并实现灵活的物体生成,开发者通常会利用预制体来作为物体的模板,然后通过脚本在运行时动态创建物体。本文将详细讲解如何通过脚本创建物体,并涵盖一些常见的技巧和方法。 预制体与实例化 预制体简介 预…