【c++】字符串 string 以及与右值结合

devtools/2025/2/13 2:29:50/

【c++】字符串 string 以及与右值结合

std::string 类介绍

std::string 是 C++ 标准库中的一个用于处理字符串的类,定义在头文件 <string> 中。std::string 封装了一个可变长度的字符数组,可以动态地存储和操作文本字符串

在使用char类型指针指向字符数据时
例如字符串”yyyyyyy“字符串存储在数据区,并且是只读属性,多个char类型指针指向同一字符串”yyyyyyy“,他们的地址是一样的。不能够对其进行修改。
如果要对其修改,使用一个可以修改的字符数组或者动态分配的内存来存储字符串

std::string 内部的基本结构通常由两个主要部分组成:

  1. 字符数组:用于存储字符串的字符数据。
  2. 大小和容量:存储当前字符串的长度(字符数)和容量(分配的内存大小)。

string 对象占据内存大小

string 对象占据内存大小 在vs2022 下面是40字节 这指的是string对象大小 与存储的数据无关
string 容量默认 在vs2022 下面是15字节

int main() {int main() {string s;cout << "-----------s-------------------" << endl;cout << "string容量:" << s.capacity() << endl;cout << "string大小:" << sizeof(s) << endl;cout << "------------s1------------------" << endl;string s1 = "abc";cout << "string容量:" << s1.capacity() << endl;cout << "string大小:" << sizeof(s1) << endl;cout << "--------------------------------" << endl;vector<string> a = { "12","34","56" };cout << sizeof(a[0]) << "\n";cout << endl;return 0;
}

在这里插入图片描述

std::string

std::string 的内存管理是其核心特性之一。它负责字符串内存的分配、扩展和释放。其内存分配通常分为两部分:字符串内容的存储空间控制信息(如长度、容量、指向字符数组的指针)

1. 内部实现

字符数据指针(char*): 指向堆上分配的内存区域,用于存储字符串的字符数据。

大小(size_t): 当前字符串的长度,即字符数据的实际使用长度。

容量(size_t): 当前分配的内存容量,即字符数据指针所指向的内存区域的大小。

当字符串的长度超过当前容量时,std::string 会在堆上重新分配更大的内存区域,并将原有的字符数据复制到新的内存区域。

2. 引用计数(Reference Counting)

标准的 std::string 并不直接使用引用计数来管理内存。但是,在一些实现(如 GCC 的 libstdc++)中,std::string 可能采用了 小对象优化(SSO,Short String Optimization)复制-写(Copy-On-Write, COW) 机制,这些机制与引用计数密切相关。

  • 小对象优化(SSO):对于小字符串,std::string 会直接在对象内部存储字符数据(而不是在堆上分配内存),以避免内存分配的开销。通常只有当字符串的长度超过一定阈值时(比如 15 )才会在堆上分配内存。

然而,现代的 C++ 标准库实现(如 C++11 以后)已经放弃了 COW 机制,主要是为了避免多线程环境中的潜在问题。现在,std::string 通常直接在每个对象中管理内存,并在发生拷贝时进行深拷贝。

在 C++ 中,std::string 类通过小字符串优化(Small String Optimization,SSO)来提高短字符串的性能。对于长度较短的字符串,std::string 会直接在对象内部存储字符数据,而不是在堆上分配内存,以避免内存分配的开销。通常,只有当字符串的长度超过一定阈值时(例如 15 个字符),std::string 才会在堆上分配内存。

#include <iostream>
#include <string>int main() {std::string shortStr = "Hello"; // 长度为 5 的短字符串std::string longStr = "This is a much longer string."; // 长度超过 15 的长字符串std::cout << "短字符串容量:" << shortStr.capacity() << std::endl;std::cout << "长字符串容量:" << longStr.capacity() << std::endl;return 0;
}

输出示例:

短字符串容量:15
长字符串容量:32

shortStr 是一个长度为 5 的短字符串,而 longStr 是一个长度超过 15 的长字符串。由于 shortStr 的长度小于 15,std::string 会在对象内部直接存储字符数据,而不是在堆上分配内存。因此,shortStr 的容量为 15。而 longStr 的长度超过了 15,std::string 会在堆上分配内存来存储字符数据,因此其容量为 32。

2. 扩展空间(Capacity)

std::string 对象的内存分配通常会预先为字符串分配一个比实际长度稍大的空间,称为“容量(capacity)”。这种容量是为了避免在字符串增长时频繁的内存重新分配,通常采用指数增长的策略,即每次容量增加时都会分配一个比之前大一倍的内存空间。

  • 大小(size):表示当前字符串的长度(字符数)。
  • 容量(capacity):表示为当前字符串分配的内存大小(以字符为单位)。容量可能大于实际的字符串长度,目的是为了优化内存分配和避免频繁的内存分配。
  • 扩展机制:当字符串需要增长时(例如插入、拼接等),如果现有容量不足以容纳新的字符,std::string 会分配一个更大的内存区域,将旧的字符数据复制到新的区域,并释放原来的内存。
#include <iostream>
#include <string>int main() {std::string s = "Hello"; // 长度为 5 的短字符串s += "This is a much longer string."; // 长度超过 15 的长字符串std::cout << "短字符串容量:" << s.capacity() << std::endl;std::cout << "长字符串容量:" << s.capacity() << std::endl;return 0;
}

s = “Hello”;
在这里插入图片描述

s += “This is a much longer string.”;
在这里插入图片描述

std::string 提供了 reserve()resize() 方法来控制容量和大小的行为:

  • reserve(n):预分配至少 n 字符的内存空间,避免在插入字符时频繁地重新分配内存。
  • resize(n):改变字符串的大小,如果增加大小,会使用默认值或指定的字符填充。

3. 深拷贝

std::string 实现了深拷贝操作,这意味着当 std::string 对象被复制时,底层的字符数据也会被复制。深拷贝确保每个 std::string 对象有自己的独立数据,并且对其中一个对象的修改不会影响其他对象。

当发生拷贝构造或拷贝赋值时,std::string 会为目标对象分配新的内存,并将源对象的数据复制到目标对象的内存区域。

示例:

#include <iostream>
#include <string>int main() {std::string str1 = "Hello, world!";std::string str2 = str1;  // 深拷贝// 修改 str1 后,str2 的值不会受到影响str1[0] = 'h';std::cout << "str1: " << str1 << std::endl;  // 输出 "hello, world!"std::cout << "str2: " << str2 << std::endl;  // 输出 "Hello, world!"return 0;
}

std::string 类的移动构造和移动赋值

std::string 在 C++11 引入了移动构造函数和移动赋值操作符来进一步提高性能,尤其是在涉及到字符串的传递和返回时,避免了不必要的内存拷贝。

4.1移动构造函数

当使用右值传递 std::string 对象时,std::string 会通过移动构造函数将源对象的字符数据“转移”到目标对象中,而不进行深拷贝。

#include <iostream>
#include <string>std::string create_large_string() {std::string large_str = "A very large string";return large_str;  // 使用移动语义返回
}int main() {std::string str = create_large_string();  // 移动构造,避免拷贝std::cout << str << std::endl;
}

4.2移动赋值操作符

当将一个 std::string 的右值赋值给另一个 std::string 时,std::string 会通过移动赋值操作符来转移资源。

#include <iostream>
#include <string>int main() {std::string str1 = "Hello";std::string str2 = "World";str2 = std::move(str1);  // 移动赋值,避免拷贝std::cout << str2 << std::endl;  // 输出 "Hello"std::cout << str1 << std::endl;  // str1 已为空,输出 ""
}

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

相关文章

2025影视泛目录站群程序设计_源码二次开发新版本无缓存刷新不变实现原理

1. 引言 本设站群程序计书旨在详细阐述苹果CMS泛目录的创新设计与实现&#xff0c;介绍无缓存刷新技术、数据统一化、局部URL控制及性能优化等核心功能&#xff0c;以提升网站访问速度和用户体验。 2. 技术概述 2.1 无缓存刷新技术 功能特点&#xff1a; 内容不变性&#x…

halcon三维点云数据处理(十四)在3D场景中去除背景的三种方式

目录 一、基于二值化去除背景二、基于参考平面去除背景三、基于参考场景去除背景四、总结 一、基于二值化去除背景 在2D空间中&#xff1a;reduce_object_model_3d_by_view (Regions, ObjectModel3DScene, [], [], ObjectModel3DReduced) 首先二值化得到region&#xff0c;然后…

对React中类组件和函数组件的理解?

React 中的 类组件 和 函数组件 是两种不同的组件定义方式&#xff0c;它们都可以用来创建 UI&#xff0c;但在语法、特性和使用场景上有一些区别。以下是它们的详细对比和理解&#xff1a; 1. 类组件&#xff08;Class Components&#xff09; 特点 基于 ES6 的类&#xff1a…

UIAbility 生命周期方法

生命周期流程图 UIAbility的生命周期官方文档地址https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/uiability-lifecycle-V13 1. onCreate(want: Want, launchParam: LaunchParam) 触发时机&#xff1a;Ability首次创建时 作用&#xff1a;初始化核心资源…

Lucene 中的并发错误:如何修复乐观并发失败

作者&#xff1a;来着 Elastic Benjamin Trent 及 Ao Li 感谢 CMU PASTA 实验室开发的确定性并发测试框架 Fray&#xff0c;我们找到了一个棘手的 Lucene 漏洞并将其修复。 是的&#xff0c;另一个修复错误博客。但这个故事有一个转折&#xff0c;一位开源英雄突然出现并拯救了…

Stylelint 如何处理 CSS 预处理器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Android Studio:键值对存储sharedPreferences

一、了解 SharedPreferences SharedPreferences是Android的一个轻量级存储工具&#xff0c;它采用的存储结构是Key-Value的键值对方式&#xff0c;类似于Java的Properties&#xff0c;二者都是把Key-Value的键值对保存在配置文件中。不同的是&#xff0c;Properties的文件内容形…

【Java 面试 八股文】MySQL 篇

MySQL 篇 1. MySQL中&#xff0c;如何定位慢查询&#xff1f;2. 那这个SQL语句执行很慢&#xff0c;如何分析呢&#xff1f;3. 了解过索引吗&#xff1f;&#xff08;什么是索引&#xff09;4. 索引的底层数据结构了解过吗&#xff1f;5. B树和B树的区别是什么呢&#xff1f;6.…