跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ string 类详解:常用接口、内存管理与模拟实现

综述由AI生成C++ string 类详解涵盖常用构造方法、容量操作、访问遍历及修改接口。文章对比了 size 与 length 原理,解析了 reserve 与 resize 区别,并深入探讨了浅拷贝与深拷贝在资源管理中的差异。通过模拟实现 String 类,阐述了写时拷贝(COW)机制及其在 Linux fork、Redis bgsave 等场景的应用。内容包含 auto 关键字用法、范围 for 循环、迭代器操作以及 VS 与 G++ 下 string 结构差异分析,适合希望深入理解 C++ 标准库字符串实现的开发者参考。

修罗发布于 2026/3/21更新于 2026/4/306 浏览
C++ string 类详解:常用接口、内存管理与模拟实现

C++ string 类详解

一、为什么学习 string 类?

C 语言中的字符串

在 C 语言中,字符串是以 \0 结尾的字符数组。使用 string 类可以简化字符串操作,避免手动管理内存和边界检查。

字符串面试题

常见的字符串处理问题包括字符串转整数、字符串相加等,掌握 string 类有助于高效解决此类算法题。

二、C++ 标准库中的 string 类

在使用 string 类时,必须包含 #include <string> 头文件以及 using namespace std;。

2.1)auto 和范围 for

  • auto 关键字
  1. 核心功能: auto 的核心功能是让编译器通过初始值来推导变量的类型。这意味着使用 auto 时,变量必须初始化。
auto x = 10; // x 被推导为 int
auto y = 3.14; // y 被推导为 double
auto ptr = &x; // ptr 被推导为 int*
  1. 用 auto 声明指针类型时,用 auto 和 auto* 没有任何区别,但用 auto 声明引用类型时则必须加 &。
int x = 10;
int* p = &x;
auto a = p; // a 被推导为 int*
auto* b = p; // b 被推导为 int*
// 结论:在这种情况下,a 和 b 的类型完全一致
int x = 10;
int& ref = x; // ref 是 x 的引用
auto c = ref; // c 的类型是 int (注意:引用被丢弃了!这里是值拷贝)
auto& d = ref; // d 的类型是 int& (显式声明为引用)
c = 20; // x 依然是 10,因为 c 是独立变量
d = 30; // x 变成了 30,因为 d 是 x 的别名
  1. 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错。
/*正确用法:类型一致*/
auto a = 10, b = 20; // 正确:a 是 int,b 也是 int
auto c = 1.5, d = 3.14; // 正确:c 是 double,d 也是 double
int x = 0;
auto i = x, &j = x; // 正确:基础类型都是 int

/*错误用法:类型不一致*/
// 错误示例:编译器会报错
auto a = 10, b = 3.14; // 报错原因:编译器对 a 推导为 int,但对 b 推导为 double
auto m = 5, *p = &m; // 正确:基础类型都是 int
auto n = 5, q = 3.14; // 错误:基础类型不同
  1. auto 不能作为函数的参数,可以做返回值,但是建议谨慎使用。
// 错误示例 (C++17 及以前)
void func(auto x) { ... }

// 正确方案:模板
template<typename T>
void func(T x) { ... }
  1. auto 不能直接用来声明数组。
int arr[] = {1, 2, 3}; // 正确:传统的数组声明
auto a[] = {1, 2, 3}; // 错误!编译器无法推导出 a 是一个数组类型
auto b[3] = {1, 2, 3}; // 错误!即使指定了长度也不行

推导为指针

int nums[] = {10, 20, 30};
auto p = nums; // 正确:p 的类型被推导为 int*,而不是 int[3]

如果你希望保留数组的'大小信息'而不是让它退化成指针,可以使用引用:

int nums[] = {1, 2, 3};
auto& refArr = nums; // 正确:refArr 的类型是 int(&)[3]
  • for 关键字

C++11 中引入了基于范围的 for 循环。for 循环后的括号由冒号':'分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

  1. 语法结构
for(declaration : range) {
    // 循环体
}
  1. 代码示例
int arr[] = {1, 2, 3, 4, 5};
for(int x : arr) {
    cout << x << " ";
}
  1. 范围 for 可以作用到数组和容器对象上进行遍历。

  2. 使用示例

#include<iostream>
#include<string>
#include<map>
using namespace std;

int main() {
    int array[] = {1, 2, 3, 4, 5};
    // C++98 的遍历
    for(int i = 0; i < sizeof(array)/sizeof(array[0]); ++i) {
        array[i] *= 2;
    }
    for(int i = 0; i < sizeof(array)/sizeof(array[0]); ++i) {
        cout << array[i] << endl;
    }
    // C++11 的遍历
    for(auto& e : array) e *= 2;
    for(auto e : array) cout << e << " " << endl;
    
    string str("hello world");
    for(auto ch : str) {
        cout << ch << " ";
    }
    cout << endl;
    return 0;
}

关键点说明:

  • auto& e:e 是当前元素的引用。使用引用是因为我们需要修改 array 中的原始值。
  • ::分隔符,左边是变量,右边是范围(即数组 array)。

范围 for 不仅能作用于数组,也能作用于容器对象(如 std::string)。循环会自动调用 str.begin() 和 str.end(),依次提取字符并打印,无需手动管理迭代器或下标。

2.2)string 类的常用接口

🚩1)string 类的常用构造
构造函数功能说明
string()(重点)构造空的 string 类对象,即空字符串
string(const char* s)(重点)用 C-string 来构造 string 类对象
string(size_t n, char c)string 类对象中包含 n 个字符 c
string(const string& s)(重点)拷贝构造函数
#include<iostream>
#include<string>
using namespace std;

int main() {
    // 1. string() (重点)
    string s1;
    cout << "s1 (空字符串): [" << s1 << "]" << endl;
    
    // 2. string(const char* s) (重点)
    string s2("Hello World");
    cout << "s2 (由字符串常量构造): " << s2 << endl;
    
    // 3. string(size_t n, char c)
    string s3(10, '*');
    cout << "s3 (10 个星号): " << s3 << endl;
    
    // 4. string(const string& s) (重点)
    string s4(s2);
    cout << "s4 (拷贝自 s2): " << s4 << endl;
    return 0;
}
🚩2)string 类对象的容量操作
函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty(重点)检测字符串是否为空串,是返回 true,否则返回 false
clear(重点)清空有效字符
reserve(重点)为字符串预留空间
resize(重点)将有效字符的个数改成 n 个,多出的空间用字符 c 填充
❗注意事项
  1. size() 与 length() 底层原理相同 size() 是为了与其他容器接口保持一致。
string s = "hello";
cout << "size: " << s.size() << endl; // 输出 5
cout << "length: " << s.length() << endl; // 输出 5
  1. clear() 是清空有效字符,但不改变底层空间大小(capacity)

  2. resize(size_t n, char c) 改变有效字符个数

s = "hello";
// 如果 n 大于当前大小,用字符 'x' 填充
s.resize(10, 'x');
cout << "resize(10, 'x'): " << s << endl; // helloxxxxx
// 如果 n 小于当前大小,会发生截断,但底层总空间不变
s.resize(3);
cout << "resize(3): " << s << endl; // hel
  1. reserve(size_t res_arg) 为 string 预留空间,不改变有效元素个数;只有当参数大于当前底层空间时才会扩容
s.reserve(100);
cout << "reserve(100) 后大小:" << s.size() << endl; // 依然是 3
🚩3)string 类对象的访问及遍历操作
函数名称功能说明
operator[pos](重点)operator 是字符串名,返回 pos 位置的字符,const string 类对象调用
begin + endbegin 获取一个字符的迭代器 + end 获取最后一个字符下一个位置的迭代器
rbegin + rendrbegin 获取一个字符的迭代器 + rend 获取最后一个字符下一个位置的迭代器
范围 forC++11 支持更简洁的范围 for 的新遍历方式
#include<iostream>
#include<string>
using namespace std;

int main() {
    string s = "Hello";
    
    // 1. operator[] (下标访问)
    cout << "下标为 1 的字符:" << s[1] << endl; // 'e'
    s[0] = 'h'; // 也可以修改
    
    // 2. 使用迭代器遍历 (begin + end)
    cout << "正向迭代器遍历:";
    for(string::iterator it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // 3. 反向迭代器遍历 (rbegin + rend)
    cout << "反向迭代器遍历:";
    for(string::reverse_iterator rit = s.rbegin(); rit != s.rend(); ++rit) {
        cout << *rit << " "; // 输出 o l l e h
    }
    cout << endl;
    
    // 4. 范围 for (C++11 遍历新方式)
    cout << "范围 for 遍历:";
    for(char c : s) {
        cout << c << "-";
    }
    cout << endl;
    return 0;
}

迭代器说明:

  • string::iterator it = s.begin():string:: 是作用域限定符,iterator 是类型名,it 是变量名,s.begin() 是初始赋值。
  • string::reverse_iterator rit = s.rbegin():string::reverse_iterator 是专门用于从后往前读字符串的类型。

对于 string 来说,最常用的四种组合:

  1. iterator:能读能改,正着走。
  2. const_iterator:只能读,正着走。
  3. reverse_iterator:能读能改,倒着走。
  4. const_reverse_iterator:只能读,倒着走。
🚩4)string 类对象的修改操作
函数名称功能说明
push_back在字符串后尾插字符 c
append在字符串后追加一个字符串
operator+=(重点)在字符串后追加字符串 str
c_str(重点)返回 C 格式字符串
find + npos(重点)从字符串 pos 位置开始往后找字符 c,返回该字符在字符串中的位置
rfind从字符串 pos 位置开始往前找字符 c,返回该字符在字符串中的位置
substr在 str 中从 pos 位置开始,截取 n 个字符,然后将其返回
#include<iostream>
using namespace std;

int main() {
    // 0. 初始化
    string s = "Hello";
    
    // 1. push_back: 只能插入单个字符
    s.push_back('!'); // s 变为 "Hello!"
    
    // 2. append: 追加字符串
    s.append(" Welcome"); // s 变为 "Hello! Welcome"
    
    // 3. operator+= (重点): 最常用的追加方式
    s += " to C++"; // s 变为 "Hello! Welcome to C++"
    
    // 4. c_str (重点): 返回 C 风格字符串 (const char*)
    const char* cStr = s.c_str();
    cout << "C-Style: " << cStr << endl;
    
    // 5. find + npos (重点): 查找子串
    size_t pos = s.find("Welcome");
    if(pos != string::npos) {
        cout << "Found 'Welcome' at: " << pos << endl;
    }
    
    // 6. rfind: 从右向左找
    size_t last_space = s.rfind(' ');
    cout << "Last space at: " << last_space << endl;
    
    // 7. substr: 截取子串
    string sub = s.substr(7, 7); // 截取 "Welcome"
    cout << "Sub-string: " << sub << endl;
    return 0;
}
❗注意事项
  1. 追加字符的三种方式
char c = '!';
// 方式 A: 使用 push_back(c)
s.push_back(c);
// 方式 B: 使用 append(n, c)
s.append(1, c);
// 方式 C: 使用 += (最常用)
s += c; // 连接字符
s += " Hello"; // 连接字符串
  1. 使用 reserve 预留空间
string s;
// 如果预估要存 50 个字符,先预留空间可以减少频繁扩容带来的性能消耗
s.reserve(50);
🚩5)string 类非成员函数
函数功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>(重点)输入运算符重载
operator<<(重点)输出运算符重载
getline(重点)获取一行字符串
relational operators(重点)大小比较
string full_name;
cout << "请输入你的全名:";
gline(cin, full_name);
cout << "你好," << full_name << endl;
string str1 = "apple";
string str2 = "banana";
if(str1 < str2) {
    cout << "apple 在字典中排在 banana 前面" << endl;
}
if(str1 == "apple") {
    cout << "字符串相等" << endl;
}
🚩6)vs 和 g++ 下 string 结构的说明

注意: 下述结构是在 32 位平台下进行验证,32 位平台下指针占 4 个字节。

VS 下 string 的结构

string 总共占 28 个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string 中字符串的存储空间:

  • 当字符串长度小于 16 时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于 16 时,从堆上开辟空间

这种设计也是有一定道理的,大多数情况下字符串的长度都小于 16,那 string 对象创建好之后,内部已经有了 16 个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个 size_t 字段保存字符串长度,一个 size_t 字段保存从堆上开辟空间总的容量。 最后:还有一个指针做一些其他事情。 故总共占 16 + 4 + 4 + 4 = 28 个字节。

G++ 下 string 的结构

G++ 下,string 是通过写时拷贝实现的,string 对象总共占 4 个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
  • 指向堆空间的指针,用来存储字符串

2.3)string 类的模拟实现

上面已经对 string 类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让学生自己来模拟实现 string 类,最主要是实现 string 类的构造、拷贝构造、赋值运算符重载以及析构函数。

为了和标准库区分,此处使用 String。

class String {
public:
    String(const char* str = "") {
        if(nullptr == str) {
            assert(false);
            return;
        }
        _str = new char[strlen(str)+1];
        strcpy(_str, str);
    }
    ~String() {
        if(_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};

// 测试
void TestString() {
    String s1("hello bit!!!");
    String s2(s1);
}

这段代码展示了一个初学者在模拟实现 C++ String 类时经常会遇到的一个经典问题:浅拷贝导致的崩溃。

虽然你定义了构造函数和析构函数,但你漏掉了一个非常关键的角色:拷贝构造函数。

String s1("hello bit!!!");
String s2(s1); // 这里触发了隐式的拷贝构造
  1. s1 的创建:调用构造函数,在堆上申请了一块内存存储 "hello bit!!!",_str 指向这块地址。
  2. s2 的创建:因为你没有自己写拷贝构造函数,编译器会为你生成一个默认的。默认拷贝构造函数执行的是浅拷贝,它只是简单地把 s1._str 的值(地址)赋值给了 s2._str。
  3. 析构时的灾难:
    • 当 TestString 结束时,s2 先析构,调用 delete[] _str,释放了内存。
    • 随后 s1 析构,它也尝试调用 delete[] _str 去释放同一块内存。
    • 结果:同一块内存被释放了两次,程序直接崩溃。

解决方案 你需要手动实现拷贝构造函数,为新对象申请独立的内存空间。

String(const String& s) {
    _str = new char[strlen(s._str)+1];
    strcpy(_str, s._str);
}
🚩1)经典的 string 类问题
1)构造函数的缺省值设置非常关键
String(const char* str = "\0") // 为什么错?
  • 逻辑冗余: 在 C/C++ 中,双引号括起来的字符串字面量(如 "")结尾自带一个隐式的 \0。如果你写 "\0",实际上这个字符串在内存中包含了两个 \0。
String(const char* str = nullptr) // 为什么错?
  • 解引用空指针: string 类的构造函数内部通常会调用 strlen(str) 来计算传入字符串的长度。
  • 底层崩溃: strlen 函数的原理是持续向后读取内存直到遇到 \0。如果 str 是 nullptr(空指针),strlen 试图访问地址为 0 的内存,这会导致非法访问(Segmentation Fault)。
🚩2)浅拷贝 VS 深拷贝
传统 vs 现代 的 string 类
1)传统
class String {
public:
    String(const char* str = "") {
        if(nullptr == str) {
            assert(false);
            return;
        }
        _str = new char[strlen(str)+1]; //分配空间,+1 用于存放'\0'
        strcpy(_str, str);
    }
    String(const String& s) : _str(new char[strlen(s._str)+1]) {
        strcpy(_str, s._str);
    }
    // 赋值运算符重载 /*避免 s1 = s2 出现浅拷贝问题*/
    String& operator=(const String& s) {
        if(this != &s) {
            char* Pstr = new char[strlen(s._str)+1];
            strcpy(Pstr, s._str);
            delete[] _str;
            _str = Pstr;
        }
        return *this; // 支持链式赋值
    }
    ~String() {
        if(_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};
2)现代
class String {
public:
    /*构造函数*/
    String(const char* str = "") {
        if(str != nullptr) {
            _str = new char[strlen(str)+1];
            strcpy(_str, str);
        } else {
            assert(false);
            return;
        }
    }
    /*拷贝构造函数*/
    String(const String& s) : _str(nullptr) {
        String strTmp(s._str);
        swap(_str, strTmp._str);
    }
    /*赋值运算符重载*/
    String& operator=(String s) {
        std::swap(_str, s._str); // 直接交换形参和当前的资源
        return *this; // s 是局部副本,函数结束时自动释放旧资源
    }
    /*另一种赋值实现 (传引用方式)*/
    // String& operator=(const String& s) {
    //     if(this != &s) {
    //         String strTmp(s);
    //         std::swap(_str, strTmp._str);
    //     }
    //     return *this;
    // }
    /*析构函数*/
    ~String() {
        if(_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};

调用了 String 类的另一个构造函数(通常是 String(const char* str))。 strTmp 对象在被创建时,已经完美地做好了内存申请和数据拷贝工作(意味着你向编译器保证不会修改 s)。

交换指针:swap(_str, strTmp._str);

🚩3)写时拷贝
一、什么是写时拷贝(COW)?

在了解 COW 之前,我们先回顾一下传统的两种数据拷贝方式:

  1. 浅拷贝(Shallow Copy): 多个指针指向同一块内存。优点是速度快、省内存;缺点是一旦某个指针修改了数据,其他指针看到的数据也会跟着变,不安全。
  2. 深拷贝(Deep Copy): 重新分配一块同样大小的内存,把数据原封不动地搬过去。优点是绝对安全,互不影响;缺点是极其消耗时间和内存资源。

写时拷贝(Copy-on-Write)其实就是一种结合了浅拷贝和深拷贝优点的'懒汉(Lazy)'策略:

  • 读操作时: 大家共享同一块内存(类似浅拷贝),不产生任何额外的开销。
  • 写操作时: 当有人试图修改这块共享内存的数据时,系统才会真正分配一块新内存,把原来的数据复制过来,然后再进行修改(类似深拷贝)。

核心思想只有四个字:延迟执行。 只要你没去改它,大家就一直共享;只有在迫不得已(要修改)的时候,才去真正做拷贝的动作。

二、写时拷贝的工作机制

我们以内存页(Page)为例,看看 COW 在操作系统中是如何运转的:

  1. 初始状态: 进程 A 拥有一块内存页 Page 1。
  2. 发生拷贝(如 fork 进程): 此时需要拷贝出一个进程 B。系统不会真的去复制 Page 1,而是让进程 A 和 B 的页表都指向 Page 1,并将 Page 1 的权限设置为 只读(Read-Only)。
  3. 安全读取: 只要 A 和 B 只是读取数据,系统相安无事。
  4. 触发写入: 假设进程 B 要修改 Page 1 的数据。此时 CPU 发现 Page 1 是只读的,就会触发一个 缺页异常(Page Fault)。
  5. 内核介入(写时拷贝): 操作系统内核捕获到异常,发现这其实是一个 COW 页面。于是内核会默默分配一块新的物理内存(Page 2),把 Page 1 的数据复制到 Page 2 中,将进程 B 的页表映射到 Page 2,并将两者的权限都恢复为 可读写(Read-Write)。
  6. 继续执行: 进程 B 的写入操作在 Page 2 上顺利完成,进程 A 的数据依然在 Page 1 上保持原样。
三、写时拷贝的经典应用场景

COW 思想的应用极其广泛,以下是几个面试常考、工作中也常遇到的经典场景:

  1. Linux 中的 fork() 系统调用 在早期的 Unix 系统中,fork() 创建子进程时会把父进程的所有内存空间全部深拷贝一份。但大多数情况下,子进程马上就会调用 exec() 去执行全新的程序,之前费时费力拷贝过来的内存完全浪费了。 引入 COW 后,fork() 瞬间变得轻量级:父子进程初始共享全部物理内存。只有当某一方试图修改数据(如修改变量)时,才会针对那一小块特定的内存页进行拷贝。这极大地提升了进程创建的速度,并节省了内存。

  2. Redis 的 bgsave 持久化 Redis 是单线程的,如果在主线程里把庞大的内存数据写入磁盘(RDB 镜像),会严重阻塞正常的客户端请求。 Redis 的做法是:调用 fork() 创建一个子进程去专门负责写磁盘。得益于操作系统的 COW 机制,子进程在创建瞬间就拥有了和父进程一模一样的内存视图,而且速度极快。父进程依然可以毫无阻塞地处理客户端的新请求,只有被修改的数据页才会发生实际的内存拷贝。

  3. Java 中的 CopyOnWriteArrayList 这是 Java 并发包(java.util.concurrent)中提供的一个线程安全的 List。 它的内部实现原理是:任何读操作都不加锁,直接读取底层数组;而一旦有写操作(如 add、set、remove),就会先将原数组复制一份出一个新数组,在新数组上进行修改,修改完成后,再把原数组的引用指向新数组。

    • 适用场景: 读多写少的并发场景(如黑白名单、系统配置缓存等)。
  4. 容器技术与存储(Docker / ZFS) Docker 镜像的分层存储(如 OverlayFS、AUFS)也深度依赖了 COW。当我们启动一个容器时,底层镜像层是只读的。只有当我们在容器内修改或新建文件时,系统才会把该文件从底层镜像层复制到最上面的可写层(Container Layer)进行修改。这使得成百上千个容器可以共享同一份底层镜像,极大地节省了磁盘空间。

四、写时拷贝的优缺点总结
🌟 优点:
  1. 极大地节省内存/磁盘资源: 只有在发生修改时才去分配资源,未修改的部分永远共享。
  2. 显著提升性能: 避免了不必要的、耗时的初始化全量拷贝操作。
  3. 并发读安全: 在某些语言级别实现中(如 Java),由于原始数据不可变,天然支持无锁的并发读取。
⚠️ 缺点:
  1. 不可预知的延迟: 拷贝动作被延迟到了写操作触发的瞬间。如果数据量大,突然触发的大量写操作会导致系统出现短暂的卡顿。
  2. 写放大开销: 在 Java 的 CopyOnWriteArrayList 中,哪怕只修改数组中的一个元素,也要把整个数组复制一遍,写操作的开销极大。

2.4)面试中 string 的一种正确写法

C++ 的一个常见面试题是让你实现一个 String 类,限于时间,不可能要求具备 std::string 的功能,但至少要求能正确管理资源。具体来说:

  1. 能像 int 类型那样定义变量,并且支持赋值、复制
  2. 能用作函数的参数类型及返回类型
  3. 能用作标准库容器的元素类型,即 vector/list/deque 的 value_type

2.5)STL 中的 string 类怎么了?

关于 STL 中 string 类的具体实现细节,建议参考官方文档或深入阅读源码分析,不同编译器和版本可能存在差异。

目录

  1. C++ string 类详解
  2. 一、为什么学习 string 类?
  3. 二、C++ 标准库中的 string 类
  4. 2.1)auto 和范围 for
  5. 2.2)string 类的常用接口
  6. 🚩1)string 类的常用构造
  7. 🚩2)string 类对象的容量操作
  8. ❗注意事项
  9. 🚩3)string 类对象的访问及遍历操作
  10. 🚩4)string 类对象的修改操作
  11. ❗注意事项
  12. 🚩5)string 类非成员函数
  13. 🚩6)vs 和 g++ 下 string 结构的说明
  14. VS 下 string 的结构
  15. G++ 下 string 的结构
  16. 2.3)string 类的模拟实现
  17. 🚩1)经典的 string 类问题
  18. 1)构造函数的缺省值设置非常关键
  19. 🚩2)浅拷贝 VS 深拷贝
  20. 传统 vs 现代 的 string 类
  21. 1)传统
  22. 2)现代
  23. 🚩3)写时拷贝
  24. 一、什么是写时拷贝(COW)?
  25. 二、写时拷贝的工作机制
  26. 三、写时拷贝的经典应用场景
  27. 四、写时拷贝的优缺点总结
  28. 🌟 优点:
  29. ⚠️ 缺点:
  30. 2.4)面试中 string 的一种正确写法
  31. 2.5)STL 中的 string 类怎么了?
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 主流 AI 编程工具对比:TRAE、Qoder、Cursor 与 GitHub Copilot
  • Visual Studio 17.14 GitHub Copilot 模型管理与自定义接入
  • 基于 OpenCode + OpenSpec 的企业级 AI Coding 工程化落地实践
  • 2025 年 12 月 GESP C++ 一级真题解析
  • C++ map 与 set 容器详解:从原理到实战
  • 无线联邦学习:隐私保护下的 AI 协同进化
  • Ubuntu 20.04 系统无声问题排查与解决方案
  • Redis 分布式锁的 Java 实现与核心原理
  • 模拟算法:核心概念与经典例题实践
  • 国产时序数据库解析与金仓融合多模架构实践
  • Spring Boot 集成 WebSocket 实现后台向前端推送信息
  • C++ 特殊类设计与类型转换
  • 分治算法实战:快速排序与归并排序
  • C++11 详解:列表初始化、右值引用与移动语义
  • MySQL 联合查询详解:JOIN 类型与多表关联实战
  • Ubuntu 下 Python 连接 KingbaseES 数据库实现增删改查
  • 滑动窗口算法实战:水果成篮与最小覆盖子串
  • Android Jetpack LiveData 与 Lifecycle 源码深度解析
  • 大模型加速落地汽车领域,车企探索智能化新路径
  • CUDA Python 底层绑定与 GPU 并行计算实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online