C++ string 类从原理到实战

C++ string 类从原理到实战

一、引言

在 C++ 编程中,string是处理字符串的核心类,相较于 C 风格字符数组,它自动管理内存、提供丰富操作接口,极大提升了开发效率与代码安全性。本文将从深浅拷贝原理string底层模拟实现、标准库string常用函数详解、迭代器与容器实战、字符串数字相加案例等维度,全面梳理string类的核心知识,形成从原理到实战的完整体系。

二、浅拷贝与深拷贝的基本概念

2.1 浅拷贝

浅拷贝仅复制对象成员变量的值,若成员包含指针,仅拷贝指针地址,使多个对象指向同一块内存

  • 问题:对象析构时,同一块内存会被重复释放,导致程序崩溃或悬空指针。
  • 本质:位拷贝,只拷贝指针,不拷贝指向的数据。

2.2 深拷贝

深拷贝会为指针成员重新分配独立内存,并复制原数据,使每个对象拥有专属内存空间,互不干扰。

  • 优势:避免内存重复释放、数据篡改等问题,是string类的默认拷贝机制。
  • 本质:重新开辟内存 + 数据拷贝。

三、C++ 中 string 类的默认行为

C++ 标准库std::string的拷贝构造函数、赋值运算符均实现深拷贝,修改一个对象不会影响另一个:

#include <iostream> #include <string> using namespace std; int main() { string original = "Hello, World!"; string copy1(original); // 拷贝构造(深拷贝) string copy2 = original; // 赋值运算符(深拷贝) copy1[0] = 'h'; // 修改copy1,original、copy2不受影响 cout << original << endl; // Hello, World! cout << copy1 << endl; // hello, World! cout << copy2 << endl; // Hello, World! return 0; } 

四、自定义字符串类 MyString 实现(深浅拷贝 + 核心功能)

4.1 基础框架(构造、析构、深浅拷贝)

#include <iostream> #include <cstring> #include <assert.h> using namespace std; class MyString { private: char* _str; size_t _size; size_t _capacity; public: // 构造函数 MyString(const char*) { if (str == nullptr); _size = strlen(str); _capacity = _size; _str = new char[_capacity + 1]; strcpy(_str, str); } // 拷贝构造(深拷贝) MyString(const MyString& other) { _size = other._size; _capacity = other._capacity; _str = new char[_capacity + 1]; memcpy(_str, other._str, _size + 1); } // 赋值运算符(现代写法) MyString& operator=(MyString tmp) { swap(tmp); return *this; } // 移动赋值(C++11,转移资源) MyString& operator=(MyString&& other) noexcept { if (this != &other) { delete[] _str; _str = other._str; _size = other._size; _capacity = other._capacity; other._str = nullptr; } return *this; } // 析构函数 ~MyString() { delete[] _str; _str = nullptr; _size = _capacity = 0; } // 交换成员 void swap(MyString& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } }; 

4.2 比较运算符重载(> < >= <= == !=)

基于strcmp实现字典序比较:

bool operator>(const MyString& other) const { return strcmp(_str, other._str) > 0; } bool operator<(const MyString& other) const { return strcmp(_str, other._str) < 0; } bool operator>=(const MyString& other) const { return !(*this < other); } bool operator<=(const MyString& other) const { return !(*this > other); } bool operator==(const MyString& other) const { return strcmp(_str, other._str) == 0; } bool operator!=(const MyString& other) const { return !(*this == other); } 

4.3 resize 函数实现

调整字符串长度,支持缩小、扩大与填充字符:

void resize(size_t new_size, char fill_char = '\0') { if (new_size < _size) { _size = new_size; _str[_size] = '\0'; } else { reserve(new_size); for (size_t i = _size; i < new_size; ++i) { _str[i] = fill_char; } _size = new_size; _str[_size] = '\0'; } } // 预留容量 void reserve(size_t n) { if (n > _capacity) { char* new_str = new char[n + 1]; strcpy(new_str, _str); delete[] _str; _str = new_str; _capacity = n; } } 

4.4 + 运算符重载(字符串拼接)

支持MyString + MyStringMyString + const char*

MyString operator+(const MyString& other) const { size_t new_size = _size + other._size; char* new_data = new char[new_size + 1]; strcpy(new_data, _str); strcat(new_data, other._str); MyString res(new_data); delete[] new_data; return res; } MyString operator+(const char* str) const { size_t len = strlen(str); size_t new_size = _size + len; char* new_data = new char[new_size + 1]; strcpy(new_data, _str); strcat(new_data, str); MyString res(new_data); delete[] new_data; return res; } 

4.5 迭代器实现(简单模拟)

string迭代器本质是字符指针:

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 常用函数详解

5.1 初始化方式

string s0; // 空字符串 string s1("hello"); // 字面量初始化 string s2 = s1; // 拷贝初始化 string s3(5, 'a'); // 重复字符:"aaaaa" 

5.2 容量与长度

  • size()/length():返回字符串长度(功能完全一致)
  • capacity():返回已分配内存容量
  • reserve(n):预分配容量,减少扩容
  • shrink_to_fit():释放多余内存,使容量等于长度
  • clear():清空字符串内容,不释放内存

5.3 字符串操作

  1. 拼接+=append()
  2. 查找find()rfind()find_first_of()(未找到返回string::npos
  3. 子串substr(pos, len)(省略len取到末尾)
  4. 替换replace(pos, len, str)
  5. 插入 / 删除insert()erase()(效率低,少用)
  6. 字符访问[](不检查越界)、at()(越界抛异常)

5.4 数值与字符串转换

  • 数值→字符串:to_string()(C++11,标准通用)
  • 字符串→整数:atoi()(C 风格)、stoi()(C++11,抛异常)
  • 通用转换:stringstream(跨平台、支持任意类型)

5.5 输入输出

  • cin:以空格 / 换行分隔,无法读取带空格字符串
  • getline(cin, str):读取整行,包含空格(需处理前导换行符)
cin.ignore(); // 清空缓冲区换行 getline(cin, str); 

六、迭代器与容器配合

6.1 string 遍历方式

  1. 下标遍历
for (size_t i = 0; i < s.size(); ++i) cout << s[i]; 
  1. 迭代器遍历
string::iterator it = s.begin(); while (it != s.end()) cout << *it++; 
  1. 反向迭代器
string::reverse_iterator rit = s.rbegin(); while (rit != s.rend()) cout << *rit++; 
  1. 范围 for 循环(简洁推荐)
for (auto& ch : s) cout << ch; 

6.2 STL 算法配合

需包含<algorithm>

reverse(s.begin(), s.end()); // 反转 sort(s.begin(), s.end()); // 排序 

七、实战案例:字符串数字相加

处理超大数字(超出整型范围),模拟手工加法:

#include <iostream> #include <string> #include <algorithm> using namespace std; string addStrings(string num1, string num2) { int i = num1.size() - 1, j = num2.size() - 1; string res; int carry = 0; while (i >= 0 || j >= 0 || carry) { int val1 = i >= 0 ? num1[i--] - '0' : 0; int val2 = j >= 0 ? num2[j--] - '0' : 0; int sum = val1 + val2 + carry; carry = sum / 10; res.push_back(sum % 10 + '0'); } reverse(res.begin(), res.end()); return res; } // 测试 int main() { cout << addStrings("123456789", "987654321") << endl; // 1111111110 return 0; } 

八、总结

  1. 深浅拷贝:浅拷贝共享内存易崩溃,深拷贝独立内存更安全,string默认深拷贝。
  2. 自定义string:需实现构造、析构、拷贝构造、赋值运算符,重载常用运算符与迭代器。
  3. 标准string:接口丰富,优先使用+=reserve优化性能,注意findsubstr边界。
  4. 迭代器:是 STL 容器通用访问方式,配合算法可高效操作字符串。
  5. 实战:字符串数字相加核心为逆序逐位相加 + 处理进位 + 反转结果,适用于超大数运算。

本文覆盖string原理、实现、使用、实战全流程,是 C++ 字符串编程的核心参考资料。

Read more

新手向:C语言、Java、Python 的选择与未来指南

新手向:C语言、Java、Python 的选择与未来指南

语言即工具,选对方向比埋头苦学更重要 你好,编程世界的新朋友!当你第一次踏入代码的宇宙,面对形形色色的编程语言,是否感到眼花缭乱?今天我们就来聊聊最主流的三种编程语言——C语言、Java 和 Python——它们各自是谁,适合做什么,以及未来十年谁能带你走得更远。 一、编程世界的三把钥匙:角色定位 如果把编程比作建造房屋,那么: * C语言是钢筋骨架:诞生于1972年,它直接与计算机硬件“对话”,负责构建最基础的支撑结构。 * Java是精装套房:1995年问世,以“一次编写,到处运行”闻名,擅长打造稳定、可复用的功能模块。 * Python是智能管家:1991年出生却在近十年大放异彩,像一位高效助手,用最少的指令完成复杂任务13。 二、核心差异对比:从底层到应用 1. 语言类型与设计哲学 * C语言:属于面向过程的编译型语言。代码在执行前需全部翻译成机器指令,运行效率极高,但需要开发者手动管理内存(类似自己打扫房间)15。 * Java:

By Ne0inhk
iv8:基于 V8 的 C++ 浏览器补环境框架

iv8:基于 V8 的 C++ 浏览器补环境框架

补环境的本质是对抗检测,靠 JS 层模拟,总会遇到难以彻底复刻的检测点,比如 document.all、跨域/跨 Context 的身份同一性等。 与其在 JS 层疲于奔命地“修修补补”,不如深入 C++ 层,在 V8 之上重筑一套原生的浏览器运行时。iv8 的监控不依赖 JS Proxy,而是通过 V8 回调封装与通用属性拦截器,实现统一的 API 访问追踪。整体架构对齐 Chromium 的 ScriptWrappable/WrapperTypeInfo,并采用基于 v8::Global<> 的强引用方案配合显式释放,避免句柄滞留导致的内存占用累积,同时也避免引入 cppgc 带来的额外复杂度。 本文是一次阶段性总结:把 iv8

By Ne0inhk
从割裂到融合:MATLAB与Python混合编程实战指南

从割裂到融合:MATLAB与Python混合编程实战指南

从割裂到融合:MATLAB与Python混合编程实战指南 摘要:在科学计算领域,MATLAB和Python就像两把各有所长的“神兵利器”——MATLAB凭借矩阵运算的“独门绝技”称霸工程仿真,Python则依靠开源生态的“人海战术”横扫AI与数据科学。但在实际研发中,单一语言往往难以覆盖全流程需求:用MATLAB做完工程仿真,想对接Python的机器学习模型;用Python训练好AI模型,又需要MATLAB做工程验证。 这种场景下,MATLAB与Python的混合编程不再是“锦上添花”,而是提升研发效率的“刚需”。本文将手把手教你打通两大语言的壁垒,从技术原理到代码实战,全方位解析跨语言协作的最优路径。 一、核心技术路径对比 在动手编码前,先理清MATLAB与Python互调的核心方案,不同场景适配不同技术: 技术方案适用场景性能部署复杂度核心优势MATLAB Engine APIPython调用MATLAB函数(开发阶段)高低(需装MATLAB)调用最直接,支持全量MATLAB功能MATLAB Compiler SDKMATLAB代码打包部署(生产环境)中中(需运行时

By Ne0inhk

C++ 二叉排序树

1. 二叉排序树的要求:树根的左子树,均要小于树根;树根的右子树,均要大于树根。对于任意一个结点及其子树,均要满足二叉排序树的要求。 2. 基于二叉排序树的基本特性,可知:左子树的最一定位于左子树的最右边;右子树的最大数据一定位于右子树的最左边。 3. 对于二叉排序树的构建,依次与树 / 子树的根结点进行对比,小于根结点,放在左边;大于根结点,放在右边,直到插入位置左右子树为空时停止,在该位置插入。 4. 二叉排序树的最大查找次数,就是树的深度,类似于折半查找,每查一次排除一半的树。 5. 删除二叉排序树的过程: 基于二叉树的查找,找到要删除的 root 结点,如果 root 结点没有左子树 / 没有右子树,则非常好办,直接将右子树 / 左子树覆盖 root 结点即可,不论下面多么复杂,都符合二叉排序树的要求。如果左右子树均不为空,那就进入该 root 结点的左子树,找到左子树的最大数据结点

By Ne0inhk