当前位置: 首页 > news >正文

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

【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 已为空,输出 ""
}

相关文章:

  • 每日一题——字符串的排列
  • STM32 HAL库UART串口数据接收实验
  • kibana es 语法记录 elaticsearch
  • Python--常见库与函数
  • Java使用Redis
  • Go语言中的panic
  • 数字内容体验案例解析与转化率提升策略
  • 问卷数据分析|SPSS实操之二元logistics分析
  • Matlab 多项式曲线拟合(三维)
  • 蓝桥杯 Java B 组之日期与时间计算(闰年、星期计算)
  • PHP场地预定小程序
  • LLM论文笔记 6: Training Compute-Optimal Large Language Models
  • nvm下载安装教程(node.js 下载安装教程)
  • 【自学笔记】人工智能基础知识点总览-持续更新
  • 云轴科技ZStack+神州鲲泰,全面支持企业私有化部署DeepSeek模型
  • IPC通信
  • Redis 数据类型 Zset 有序集合
  • 【第4章:循环神经网络(RNN)与长短时记忆网络(LSTM)——4.1 RNN的基本结构与工作原理】
  • AJAX 与 ASP 的深入探讨
  • 树莓集团与宜宾“双轮驱动“共启新程 数字经济集群落子川南
  • 内塔尼亚胡称将控制“整个加沙”,英、法、加威胁对以“制裁”
  • 离休干部周惠梅逝世,曾从事多年地下革命工作
  • 人民日报:不能层层加码,要层层负责
  • 19世纪的“表征之场”:弗洛伊德的精神分析椅
  • 特朗普指控FBI前局长“暗示刺杀总统”,“8647”藏着什么玄机?
  • 科普|认识谵妄:它有哪些表现?患者怎样走出“迷雾”?