【C++】string类模拟实现(超详细解析,小白必看系列)

devtools/2025/1/14 17:04:11/

模拟实现 C++ 标准库中的 std::string 类是一个很好的练习,可以帮助深入理解 C++ 的内存管理和面向对象编程。

1 浅拷贝和深拷贝解析

浅拷贝和深拷贝是对象复制中的两种不同机制,理解它们的区别对于编写高效、无误的代码至关重要。

浅拷贝 (Shallow Copy)

浅拷贝是指创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。如果属性是基本数据类型,拷贝的就是基本数据类型的值;如果属性是引用类型,拷贝的就是内存地址。因此,浅拷贝后的新对象和原对象共享同一块内存,修改一个对象可能会影响另一个对象。

示例:

#include <iostream>
#include <cstring>class ShallowCopy {
public:ShallowCopy(const char* data) {_data = new char[strlen(data) + 1];strcpy(_data, data);}ShallowCopy(const ShallowCopy& other) {_data = other._data; // 仅复制指针}~ShallowCopy() {delete[] _data;}void print() const {std::cout << _data << std::endl;}private:char* _data;
};int main() {ShallowCopy obj1("Hello");ShallowCopy obj2 = obj1; // 浅拷贝obj1.print(); // 输出: Helloobj2.print(); // 输出: Helloreturn 0;
}

深拷贝 (Deep Copy)

深拷贝是指创建一个新对象,并递归地复制所有对象的属性。新的对象与原始对象没有任何关联,修改一个对象不会影响另一个对象。

示例:

#include <iostream>
#include <cstring>class DeepCopy {
public:DeepCopy(const char* data) {_data = new char[strlen(data) + 1];strcpy(_data, data);}DeepCopy(const DeepCopy& other) {_data = new char[strlen(other._data) + 1];strcpy(_data, other._data); // 复制内容}~DeepCopy() {delete[] _data;}void print() const {std::cout << _data << std::endl;}private:char* _data;
};int main() {DeepCopy obj1("Hello");DeepCopy obj2 = obj1; // 深拷贝obj1.print(); // 输出: Helloobj2.print(); // 输出: Helloreturn 0;
}

区别总结

  • 浅拷贝:复制对象的引用,两个对象共享同一块内存,修改一个对象会影响另一个对象。
  • 深拷贝:复制对象的内容,两个对象独立存在,修改一个对象不会影响另一个对象。

2 代码实现

1. 引入必要的头文件

#include <cstring> // 用于strlen, strcpy, strcmp, memmove, memcpy, memset等函数
#include <cassert> // 用于assert断言检查
#include <algorithm> // 用于std::swap交换函数

2. 类定义及类型别名

class string {
public:typedef char* iterator; // 定义迭代器类型,方便遍历字符串

3. 构造函数

    // 默认构造函数,初始化为空字符串或给定的C风格字符串string(const char* str = "") {_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}

4. 复制构造函数与赋值操作符

    // 复制构造函数,使用copy-and-swap技巧string(const string& s) : _str(nullptr), _size(0), _capacity(0) {string tmp(s._str); // 创建一个临时对象this->swap(tmp); // 与临时对象交换数据}// 复制赋值操作符,同样使用copy-and-swap技巧string& operator=(string s) {this->swap(s); // 与传入的对象交换数据return *this;}

5. 析构函数

    // 析构函数,释放分配的内存~string() {if (_str) {delete[] _str;_str = nullptr;}}

6. 迭代器支持

    // 迭代器支持,用于遍历字符串iterator begin() { return _str; }iterator end() { return _str + _size; }

7. 修改操作

    // 在字符串末尾添加一个字符void push_back(char c) {if (_size == _capacity)reserve(_capacity * 2); // 如果容量不足,增加容量_str[_size++] = c;_str[_size] = '\0'; // 空终止符}// 重载+=操作符,用于添加单个字符string& operator+=(char c) {push_back(c);return *this;}

8. 字符串附加操作

    // 附加一个C风格字符串void append(const char* str) {size_t len = strlen(str);if (_size + len > _capacity)reserve(_size + len); // 确保有足够的容量strcat(_str, str); // 追加字符串_size += len;}// 重载+=操作符,用于附加C风格字符串string& operator+=(const char* str) {append(str);return *this;}

9. 清空字符串

    // 清空字符串void clear() {_size = 0;_str[_size] = '\0'; // 空终止符}

10. 交换操作

    // 交换两个字符串的数据void swap(string& s) {std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}

11. 获取C风格字符串

    // 获取C风格字符串const char* c_str() const {return _str;}

12. 容量相关操作

    // 获取字符串的大小、容量和是否为空size_t size() const { return _size; }size_t capacity() const { return _capacity; }bool empty() const { return _size == 0; }// 调整字符串的大小void resize(size_t newSize, char c = '\0') {if (newSize > _size) {if (newSize > _capacity)reserve(newSize); // 确保有足够的容量memset(_str + _size, c, newSize - _size);}_size = newSize;_str[newSize] = '\0'; // 空终止符}// 预留内存空间void reserve(size_t newCapacity) {if (newCapacity > _capacity) {char* str = new char[newCapacity + 1];strcpy(str, _str); // 复制现有字符串delete[] _str; // 释放旧的内存_str = str;_capacity = newCapacity;}}

13. 访问操作符

    // 方括号访问操作符char& operator[](size_t index) {assert(index < _size);return _str[index];}const char& operator[](size_t index) const {assert(index < _size);return _str[index];}

14. 字符串比较操作符

    // 字符串比较操作符bool operator<(const string& s) {return strcmp(_str, s._str) < 0;}bool operator<=(const string& s) {return strcmp(_str, s._str) <= 0;}bool operator>(const string& s) {return strcmp(_str, s._str) > 0;}bool operator>=(const string& s) {return strcmp(_str, s._str) >= 0;}bool operator==(const string& s) {return strcmp(_str, s._str) == 0;}bool operator!=(const string& s) {return strcmp(_str, s._str) != 0;}

15. 字符串查找操作

    // 查找单个字符或子字符串size_t find(char c, size_t pos = 0) const {for (size_t i = pos; i < _size; ++i) {if (_str[i] == c) {return i;}}return string::npos; // 未找到}size_t find(const char* s, size_t pos = 0) const {size_t len = strlen(s);for (size_t i = pos; i <= _size - len; ++i) {if (strncmp(_str + i, s, len) == 0) {return i;}}return string::npos; // 未找到}

16. 插入操作

    // 在指定位置插入单个字符或子字符串string& insert(size_t pos, char c) {if (pos > _size) {throw std::out_of_range("Position out of range");}if (_size + 1 > _capacity) {reserve(_size + 1); // 确保有足够的容量}memmove(_str + pos + 1, _str + pos, _size - pos + 1);_str[pos] = c;++_size;return *this;}string& insert(size_t pos, const char* str) {size_t len = strlen(str);if (pos > _size) {throw std::out_of_range("Position out of range");}if (_size + len > _capacity) {reserve(_size + len); // 确保有足够的容量}memmove(_str + pos + len, _str + pos, _size - pos + 1);memcpy(_str + pos, str, len);_size += len;return *this;}

17. 删除操作

    // 删除指定位置的字符或子字符串string& erase(size_t pos, size_t len) {if (pos > _size || pos + len > _size) {throw std::out_of_range("Position out of range");}memmove(_str + pos, _str + pos + len, _size - pos - len + 1);_size -= len;return *this;}

18. 友元函数:输入输出流操作

private:friend ostream& operator<<(ostream& _cout, const string& s); // 输出流操作符friend istream& operator>>(istream& _cin, string& s); // 输入流操作符char* _str;size_t _capacity;size_t _size;
};

19. 输出流操作符

// 输出流操作符,用于输出字符串
ostream& operator<<(ostream& _cout, const string& s) {for (size_t i = 0; i < s.size(); ++i) {_cout << s[i];}return _cout;
}

以上是按功能块拆分的代码,每一块都有对应的中文注释解释其功能和作用。


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

相关文章

【ARM】如何通过 KeilMDK 查看芯片的硬件信息

【更多软件使用问题请点击亿道电子官方网站】 文档目标&#xff1a;解决在开发过程中对于开发项目所使用的的芯片的参数查看的问题 问题场景&#xff1a;在项目开发过程中&#xff0c;经常需要对于芯片的时钟、寄存器或者一些硬件参数需要进行确认。大多数情况下是需要外部查找…

12、xinference部署与自定义模型

1、环境创建 创建虚拟环境 conda create --name xinference python3.10.9激活虚拟环境 conda activate xinference2、安装文件 官网&#xff1a;https://inference.readthedocs.io/zh-cn/latest/getting_started/installation.html pip install "xinference[transfor…

什么是站点内部搜索垃圾邮件攻击以及如何防范

过去一年中&#xff0c;我们发现很多WordPress网站遭遇了大规模的SEO垃圾邮件攻击&#xff0c;这些攻击主要针对网站内部的搜索功能。虽然这些攻击对SEO本身的影响不大&#xff0c;但却浪费了大量的时间和资源。 虽然大部分网站可能不需要担心这个问题&#xff0c;但如果你的网…

Linux中限制服务如mysql的最大cpu使用率

1、cpu占用测试&#xff1a; DELIMITER // DROP PROCEDURE IF EXISTS intensive_calculations; CREATE PROCEDURE intensive_calculations() BEGINDECLARE v INT DEFAULT 0;DECLARE i INT DEFAULT 0;WHILE i < 1000000 DOSET v SQRT(i * i (RAND() * 10000));SET i i 1…

Gradle和Maven

Gradle 和 Maven 都是 Java 生态中常用的构建工具&#xff0c;用于管理项目的编译、测试、依赖管理和打包等任务。两者在很多方面有相似之处&#xff0c;但也有显著的不同&#xff0c;选择使用哪个工具通常取决于项目的具体需求和团队的偏好。 Gradle 与 Maven 的比较 特性Gr…

基于SSM的二手物品交易管理系统的设计与实现 (含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的二手物品交易管理系统7拥有两种角色 管理员&#xff1a;用户管理、分类管理、商品管理、订单管理、系统管理等 用户&#xff1a;登录注册、充值、收货、评价、收藏、购物车、订…

Leetcode面试经典150题-162.寻找峰值

解法都在代码里&#xff0c;不懂就留言或者私信 想清楚的话会特别简单&#xff0c;你可能想不到这是个二分。。。 class Solution {/**本题题目规定我们只能用O(logN)的时间复杂度来解题&#xff0c;这显然就是让二分嘛而题目给的数组本身是无需&#xff0c;怎么二分呢其实我…

PHP-SER-libs靶场通关(1-9)

一.第一关&#xff08;基础序列化&#xff09; <?php highlight_file(__FILE__); class a{var $act;function action(){eval($this->act);} } $aunserialize($_GET[flag]); $a->action(); ?> 一个很基础的反序列过程 对输入的flag进行反序列化&#xff0c;再…