C++心决之stl中那些你不知道的秘密(string篇)
目录
1. 为什么学习string类?
1.1 C语言中的字符串
2. 标准库中的string类
2.1 string类
2.2 string类的常用接口说明
1. string类对象的常见构造
2. string类对象的操作
3.vs和g++下string结构的说明
3. string类的模拟实现
3.2 浅拷贝
3.3 深拷贝
3.4 写时拷贝
3.5 string类的模拟实现
1. 为什么学习string类?
1.1 C语言中的字符串
2. 标准库中的string类
2.1 string类
https://cplusplus.com/reference/string/string/?kw=string
- 1. 字符串是表示字符序列的类
- 2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
- 单字节字符字符串的设计特性。
- 3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
- 息,请参阅basic_string)。
- 4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
- 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个
- 类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
- 1. string是表示字符串的字符串类
- 2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- 3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
- string;
- 4. 不能操作多字节或者变长字符的序列。
2.2 string类的常用接口说明
1. string类对象的常见构造
void Teststring()
{string s1; // 构造空的string类对象s1string s2("hello bit"); // 用C格式字符串构造string类对象s2string s3(s2); // 拷贝构造s3
}
2. string类对象的操作
PS:
- 1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
- 2. clear()只是将string中有效字符清空,不改变底层空间大小。
- 3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。
- 4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
- string的底层空间总大小时,reserver不会改变容量大小。
3.vs和g++下string结构的说明
下述结构是在32 位平台下进行验证, 32 位平台下指针占 4个字节。vs下 string的结构string总共占 28 个字节 ,内部结构稍微复杂一点,先是 有一个联合体,联合体用来定义 string中字 符串的存储空间:
- 当字符串长度小于16时,使用内部固定的字符数组来存放
- 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty { // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing } _Bx;
这种设计也是有一定道理的,大多数情况下字符串的长度都小于 16 ,那 string 对象创建好之后,内部已经有了16 个字符数组的固定空间,不需要通过堆创建,效率高。其次:还有 一个 size_t 字段保存字符串长度,一个 size_t 字段保存从堆上开辟空间总的容量最后:还 有一个指针 做一些其他事情。故总共占16+4+4+4=28个字节。![]()
g++ 下 string 的结构G++ 下, string 是通过写时拷贝实现的, string 对象总共占 4 个字节,内部只包含了一个指针,该指 针将来指向一块堆空间,内部包含了如下字段:
- 空间总大小
- 字符串有效长度
- 引用计数
- 指向堆空间的指针,用来存储字符串。
struct _Rep_base {size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount; };
3. string类的模拟实现
PS: string类在自己实现的时候一定要注意浅拷贝问题
3.2 浅拷贝
3.3 深拷贝

3.4 写时拷贝
3.5 string类的模拟实现
//string.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace mystr {class string{public://迭代器, 因为字符串底层内存连续, 所以可以简单的定义成指针typedef char* iterator;typedef const char* const_iterator;//配合范围for循环iterator begin() { return _str; }iterator end() { return _str + _size; }//兼容常量字符串const_iterator begin() const { return _str; }const_iterator end() const { return _str + _size; }//string();string(const char* str = "");string(const string& s);string& operator=(string temp) { swap(temp); return *this; }~string() { delete[] _str; _str = nullptr; _size = _capacity = 0; }//返回C语言字符数组const char* c_str() const { return _str; }size_t size() const { return _size; }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);void push_back(char ch) { insert(_size, ch); }void append(const char* str) { insert(_size, str); }string& operator+=(char ch) { insert(_size, ch); return *this; }string& operator+=(const char* str) { insert(_size, str); return *this; };void insert(size_t pos, char ch);void insert(size_t pos, const char* str);void erase(size_t pos = 0, size_t len = npos);size_t find(char ch, size_t pos = 0) {for (size_t i = pos; i < _size; i++) if (_str[i] == ch) return i;return npos;}size_t find(const char* str, size_t pos = 0) { return strstr(_str + pos, str) - _str; }void swap(string& s);string substr(size_t pos = 0, size_t len = npos);bool operator<(const string& s) const { return strcmp(_str, s._str) < 0; }bool operator>(const string& s) const { return !(*this <= s); }bool operator<=(const string& s) const { return !(*this > s); }bool operator>=(const string& s) const { return !(*this < s); }bool operator==(const string& s) const {return strcmp(_str, s._str) == 0; }bool operator!=(const string& s) const { return !(*this == s); }void clear() { _str[0] = '\0'; _size = 0; }private:char* _str;size_t _size;size_t _capacity;//一般static变量的定义要放在类外, 整型是特例const static size_t npos = -1;};void swap(string& s1, string& s2);istream& operator>>(istream& ci, string& s);ostream& operator<<(ostream& co, string& s);
}
//string.cpp
#include "string.h"
namespace mystr {string::string(const char* str):_size(strlen(str)) {_str = new char[_size + 1];_capacity = _size;strcpy(_str, str);}string::string(const string& s) {string temp(s._str);swap(temp);}void string::reserve(size_t n) {if (_capacity < n) {char* temp = new char[n + 1];strcpy(temp, _str);delete[] _str;_str = temp;_capacity = n;}}void string::insert(size_t pos, char ch) {assert(pos <= _size);if (_size == _capacity) {size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}size_t end = _size + 1;while (end > pos) _str[end] = _str[end - 1], --end;_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str) {assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity) reserve(_size + len);size_t end = _size + len;while (end > pos + len - 1) _str[end] = _str[end - len], --end;memcpy(_str + pos, str, len);_size += len;}void string::erase(size_t pos, size_t len) {if (len > _size - pos) _str[pos] = '\0', _size = pos;else strcpy(_str + pos, _str + pos + len), _size -= len;}void string::swap(string& s) {char* temp = _str;_str = s._str;s._str = temp;std::swap(_size, s._size);}string string::substr(size_t pos, size_t len) {if (len > _size - pos) { string sub(_str + pos); return sub; }else {string sub;sub.reserve(len);for (size_t i = pos; i < pos + len; i++) sub += _str[i];return sub;}}void swap(string& s1, string& s2){ s1.swap(s2); }istream& operator>>(istream& ci, string& s) {s.clear();char ch = ci.get();while (ch != ' ' && ch != '\n') s += ch, ch = ci.get();return ci;}ostream& operator<<(ostream& co, string& s) {for (size_t i = 0; i < s.size(); i++) co << s[i];return co;}
}
//test.cpp
#include "string.h"
namespace mystr {void test1() {string s1 = "1111";string s2 = s1;cout << s1.c_str() << endl << s2.c_str() << endl;cout << s1.size() << endl;}void test2() {string s1 = "111";string s2 = "222222";s1 = s2;cout << s1.c_str() << endl;}void test3() {string s1 = "111222333";for (auto& i : s1) i += 3;cout << s1.c_str() << endl;const string s2 = "111222333";for (auto& i : s2) cout << i;cout << endl;for (size_t i = 0; i < s1.size(); i++) cout << (s1[i] += 2);cout << endl;}void test4() {string s1 = "sadfsf";s1.insert(2, '-');cout << s1.c_str() << endl;s1.insert(0, '-');cout << s1.c_str() << endl;s1.insert(2, "11111");cout << s1.c_str() << endl;s1.insert(0, "222222");cout << s1.c_str() << endl;}void test5() {string s1 = "asgfidsgf";s1.push_back('-');cout << s1.c_str() << endl;s1.append("=====");cout << s1.c_str() << endl;s1 += 'w';cout << s1.c_str() << endl;s1 += "0000";cout << s1.c_str() << endl;s1.erase(10);cout << s1.c_str() << endl;s1.erase(7, 100);cout << s1.c_str() << endl;s1.erase(3, 2);cout << s1.c_str() << endl;s1.erase(0);cout << s1.c_str() << endl;}void test6() {string s1 = "ksjfghks";cout << s1.find('h', 2) << endl;cout << s1.find("ghk", 2) << endl;cout << s1.find("ghksgs", 2) << endl;}void test7(){string s1 = "sggsdsdf";string s2 = "sdgfrgdb";cout << s1.c_str() << endl;cout << s2.c_str() << endl;swap(s1, s2);cout << s1.c_str() << endl;cout << s2.c_str() << endl;s1.swap(s2);cout << s1.c_str() << endl;cout << s2.c_str() << endl;string s3 = s1.substr(2, 5);cout << s3.c_str() << endl;}void test8() {string s1, s2;cin >> s1 >> s2;cout << s1 << endl << s2 << endl;}
}
int main() {mystr::test8();return 0;
}
相关文章:

C++心决之stl中那些你不知道的秘密(string篇)
目录 1. 为什么学习string类? 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类 2.2 string类的常用接口说明 1. string类对象的常见构造 2. string类对象的操作 3.vs和g下string结构的说明 3. string类的模拟实现 3.2 浅拷贝 3.3 深拷贝 3.4 写…...
date 命令学习
文章目录 date 命令学习1. 命令简介2. 语法参数2.1 使用语法2.2 说明2.3 参数说明 3. 使用案例:arrow_right: 星期名缩写 %a:arrow_right: 星期名全写 %A:arrow_right: 月名缩写 %b:arrow_right: 月名全称 %B:arrow_right: 日期和时间 %c:arrow_right: 世纪 %C:arrow_right: 按…...

前端vue后端java使用easyexcel框架下载表格xls数据工具类
一 使用alibaba开源的 easyexcel框架,后台只需一个工具类即可实现下载 后端下载实现 依赖 pom.xml <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependen…...
C#,开发过程中技术点GPT问答记录
6、为什么说GUI编程是事件驱动的? GUI(图形用户界面)编程是一种以图形方式构建用户界面的编程方法,它主要采用事件驱动模型进行程序逻辑的组织。在事件驱动的编程中,程序并不按照固定的顺序线性执行,而是等…...
wifi中的PSR技术
在Wi-Fi网络中,PSR(Preferred Spatial Reuse)是一种新兴技术,旨在提高频谱利用效率,特别是在高密度网络环境中。PSR通过允许多个接入点(AP)和设备在相同频谱资源上同时进行通信,从而…...

电子签章 签到 互动 打卡 创意印章 支持小程序 H5 App
电子签章 签到 互动 打卡 创意印章 支持小程序 H5 App 定制化...
Vscode插件推荐——智能切换输入法(Smart IME)
前言 相信广大程序员朋友在写代码的时候一定会遇到过一个令人非常头疼的事情——切换输入法,特别是对于那些勤于写注释的朋友,简直就是噩梦,正所谓懒人推动世界发展,这不,今天就向大家推荐一款好用的vscode插件&#…...

SpringBoot实战:轻松实现接口数据脱敏
一、接口数据脱敏概述 1.1 接口数据脱敏的定义 接口数据脱敏是Web应用程序中一种保护敏感信息不被泄露的关键措施。在API接口向客户端返回数据时,系统会对包含敏感信息(如个人身份信息、财务数据等)的字段进行特殊处理。这种处理通过应用特…...

我们水冷使制动电阻功率密度成倍增加-水冷电阻设计工厂
先进陶瓷 我们后来发现工业应用中对占用空间最小的水冷电阻器的工业需求,推出了适用于中压工业应用的水冷电阻器。它的特点是两块由具有特殊性能的先进陶瓷制成的板。 使用工业电驱动装置的一个重要好处是,可靠的再生和动态制动系统可以补充或取代传统…...
模板语法指令语法——02
//指令语法: 1.什么是指定,有什么作用? 指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式的作用语DOM 2.vue框架中的所有指令的名字都以v-开始的 3.插值是写在标签当中用的,指令…...

Comparable 和 Comparator 接口的区别
Comparable 和 Comparator 接口的区别 1、Comparable 接口1.1 compareTo() 方法 2、Comparator 接口2.1 compare() 方法 3、 Comparable 和 Comparator 的区别总结 💖The Begin💖点点关注,收藏不迷路💖 在Java中,Compa…...

Python requests爬虫
Python的requests库是一个强大且易于使用的HTTP库,用于发送HTTP请求和处理响应。它是Python中最受欢迎的网络爬虫框架之一,被广泛用于从网页中提取数据、爬取网站和进行API调用。 使用requests库,你可以轻松地发送各种HTTP请求,包…...

Docker 基本管理及部署
目录 1.Docker概述 1.1 Docker是什么? 1.2 Docker的宗旨 1.3 容器的优点 1.4 Docker与虚拟机的区别 1.5 容器在内核中支持的两种技术 1.6 namespace的六大类型 2.Docker核心概念 2.1 镜像 2.2 容器 2.3 仓库 3.安装Docker 3.1 查看 docker 版本信息 4.…...
Ubuntu下安装配置和调优Docker,支持IPV6
今天在阿贝云的免费云服务器上折腾了一番Docker的配置和优化,这家免费云服务器可真不错啊。1核1G 10G硬盘,5M带宽,配置虽然简单但够用了。作为一个免费的云服务器,阿贝云的性能可以说是非常不错的了,完全能胜任日常的开发和部署工作。 让我们开始吧。首先,简单介绍一下Docker吧…...

Proteus + Keil单片机仿真教程(六)多位LED数码管的动态显示
上一节我们通过锁存器和八个八位数码管实现了多个数码管的静态显示,这节主要讲解多位数码管的动态显示,所谓的动态显示就是对两个锁存器的控制。考虑一个问题,现在给WS位锁存器增加一个循环,让它从1111 1110到0111 1111会发生什么事情?话不多说,先上代码: #include<…...
WEB开发-HTML页面更新部分内容
1 需求 2 接口 3 示例 在HTML页面中,如果你想要改变部分内容而不是整个页面,有几种方法可以实现这一目标,主要包括: JavaScript 的 DOM 操作 JavaScript允许你动态地修改HTML文档中的元素内容。你可以使用document.getElementB…...

休息时间c++
题目描述 小杨计划在某个时刻开始学习,并决定在学习k秒后开始休息。 小杨想知道自己开始休息的时刻是多少。 输入 前三行每行包含一个整数,分别表示小杨开始学习时刻的时h、分m、秒s(h,m,s的值符合1≤h≤12,0≤m≤59,0≤s≤59)…...

zabbix 自定义监控项及触发器
1. 在zabbix客户端定义脚本 /etc/zabbix/zabbix_agent2.d/目录下创建自定义监控项脚本 ]# cat /etc/zabbix/zabbix_agent2.d/web.conf #UserParameterkey,cmd #UserParameterngx.port,sh /server/scripts/xxx.sh UserParameterngx.port,ss -lntup|grep -w *:80|wc -lUserPar…...

easyExcel 不规则模板导入数据
文章目录 前言一、需求和效果二、难点和思路三、全部代码踩坑 前言 之前分享的 EasyExcel 批量导入并校验数据,仅支持规则excel,即首行表头,下面对应数据,无合并单元格情况。 本篇主要解决问题: 模板excel 表头不在首…...

前端调试技巧(npm Link,vscode调试,浏览器调试等)
Npm Link 功能: 在本地开发npm模块的时候,我们可以使用npm link命令,将npm 模块链接到对应的运行项目中去,方便地对模块进行调试和测试 断点调试 vscode调试 Debug Vue2 Project 目标:在VSCode中调试项目代码…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...