
目录
[📚 前言](#📚 前言)
[(一)经典的 string 类问题](#(一)经典的 string 类问题)
[1. 浅拷贝](#1. 浅拷贝)
[⚠️ 经典 Bug 和问题](#⚠️ 经典 Bug 和问题)
[2. 深拷贝](#2. 深拷贝)
[⚠️ 经典 Bug 和问题](#⚠️ 经典 Bug 和问题)
[1. size () 和 capacity ()](#1. size () 和 capacity ())
[代码例子:size 和 capacity 的实现](#代码例子:size 和 capacity 的实现)
[2. operator[]](#2. operator[])
[代码例子:operator [] 的实现](#代码例子:operator [] 的实现)
[⚠️ 经典 Bug 和问题](#⚠️ 经典 Bug 和问题)
[3. c_str()](#3. c_str())
[代码例子:c_str 的实现](#代码例子:c_str 的实现)
[4. reserve()](#4. reserve())
[代码例子:reserve 的实现](#代码例子:reserve 的实现)
[5. 构造函数](#5. 构造函数)
[⚠️ 经典 Bug 和问题](#⚠️ 经典 Bug 和问题)
[6. 析构函数](#6. 析构函数)
[7. clear()](#7. clear())
[代码例子:clear 的实现](#代码例子:clear 的实现)
[8. 迭代器的简单模拟](#8. 迭代器的简单模拟)
[1. push_back()](#1. push_back())
[代码例子:push_back 的实现](#代码例子:push_back 的实现)
[⚠️ 经典 Bug 和问题](#⚠️ 经典 Bug 和问题)
[2. append()](#2. append())
[代码例子:append 的实现](#代码例子:append 的实现)
[3. operator+=](#3. operator+=)
[代码例子:operator+= 的实现](#代码例子:operator+= 的实现)
[4. insert()](#4. insert())
[代码例子:insert 的实现](#代码例子:insert 的实现)
[⚠️ 经典 Bug 和问题](#⚠️ 经典 Bug 和问题)
[5. erase()](#5. erase())
[代码例子:erase 的实现](#代码例子:erase 的实现)
[6. find()](#6. find())
[代码例子:find 的实现](#代码例子:find 的实现)
[7. substr()](#7. substr())
[代码例子:substr 的实现](#代码例子:substr 的实现)
[8. 六个比较运算符](#8. 六个比较运算符)
[9. 流输入 流提取](#9. 流输入 流提取)
[🎯 面试重点总结](#🎯 面试重点总结)
[必背考点(90% 概率考)](#必背考点(90% 概率考))
📚 前言
大家好!欢迎来到【从零开始学 C++】专栏的第八篇文章!今天我们要干一件大事 ------手撕 string 类!
为什么要模拟实现 string?因为:
-
✅ 面试必考题:90% 的 C++ 面试都会考 string 的模拟实现
-
✅ 理解深浅拷贝:这是 C++ 内存管理的核心知识点
-
✅ 掌握类的设计:学习如何设计一个完整的类
💡 新手提示:本文所有代码都可以直接运行,建议边看边敲代码!
(一)经典的 string 类问题
1. 浅拷贝
简介作用
浅拷贝是什么? 大白话讲就是:两个对象共用同一块内存。
就像两个人共用同一个钱包,一个人花了钱,另一个人的钱也少了。这就是浅拷贝的本质 ------ 只拷贝指针地址,不拷贝指针指向的内容。
代码例子:浅拷贝的问题演示
例子说明:演示默认拷贝构造函数造成的浅拷贝问题
cpp
#include <iostream>
#include <cstring>
using namespace std;
// 有问题的string类(浅拷贝版本)
class BadString {
private:
char* _str;
int _size;
int _capacity;
public:
// 构造函数
BadString(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1]; // +1是为了存'\0'
strcpy(_str, str);
}
// 析构函数
~BadString() {
cout << "析构函数被调用,释放内存:" << (void*)_str << endl;
delete[] _str;
_str = nullptr;
}
// 打印字符串
void print() {
cout << _str << endl;
}
};
int main() {
BadString s1("hello");
BadString s2 = s1; // 调用默认拷贝构造(浅拷贝!)
cout << "s1 = "; s1.print();
cout << "s2 = "; s2.print();
return 0;
}
运行结果(程序崩溃!):
cpp
s1 = hello
s2 = hello
析构函数被调用,释放内存:0x...
析构函数被调用,释放内存:0x... (同一块内存被释放两次!)
程序崩溃!
⚠️ 经典 Bug 和问题
浅拷贝的三大坑(面试必问!)
-
同一块内存被多次释放 ⭐⭐⭐⭐⭐
-
s1 析构释放了内存,s2 析构时又释放一次
-
这是最严重的问题,直接导致程序崩溃
-
-
一个对象修改影响另一个对象
cpps1[0] = 'H'; // 修改s1 // s2也变成了"Hello"!因为它们指向同一块内存 -
内存泄漏隐患
- 如果其中一个对象先被销毁,另一个对象就成了 "野指针
2. 深拷贝
简介作用
深拷贝是什么? 大白话讲就是:每个对象都有自己独立的内存。
就像每个人都有自己的钱包,你的钱花了不影响我的钱。深拷贝会:
-
为新对象申请新的内存空间
-
把原对象的数据复制到新内存中
-
两个对象完全独立,互不影响
代码例子:深拷贝的正确实现
例子说明:实现深拷贝的拷贝构造函数
cpp
#include <iostream>
#include <cstring>
using namespace std;
class GoodString {
private:
char* _str;
int _size;
int _capacity;
public:
// 构造函数
GoodString(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 深拷贝构造函数(重点!)
GoodString(const GoodString& s) {
cout << "深拷贝构造被调用!" << endl;
// 1. 申请独立的内存空间
_str = new char[s._capacity + 1];
// 2. 复制数据
strcpy(_str, s._str);
// 3. 复制其他成员变量
_size = s._size;
_capacity = s._capacity;
}
// ✅ 深拷贝赋值运算符重载(重点!)
GoodString& operator=(const GoodString& s) {
cout << "深拷贝赋值被调用!" << endl;
// 防止自赋值(比如 s1 = s1)
if (this != &s) {
// 1. 释放自己原来的内存
delete[] _str;
// 2. 申请新内存
_str = new char[s._capacity + 1];
// 3. 拷贝数据
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
// 析构函数
~GoodString() {
delete[] _str;
_str = nullptr;
}
void print() {
cout << _str << endl;
}
};
int main() {
GoodString s1("hello");
GoodString s2 = s1; // 深拷贝构造
GoodString s3("world");
s3 = s1; // 深拷贝赋值
cout << "s1 = "; s1.print();
cout << "s2 = "; s2.print();
cout << "s3 = "; s3.print();
return 0; // 正常析构,不会崩溃!
}
运行结果:
cpp
深拷贝构造被调用!
深拷贝赋值被调用!
s1 = hello
s2 = hello
s3 = hello
(程序正常退出,没有崩溃!)
⚠️ 经典 Bug 和问题
深拷贝的常见坑:
-
忘记检查自赋值 ⭐⭐⭐
cpps1 = s1; // 如果不检查,会先释放内存,然后从已释放的内存拷贝! -
先释放内存再申请内存
-
如果 new 失败抛出异常,原来的数据也丢了
-
现代写法:先申请新内存,成功后再释放旧内存
-
-
忘记给 '\0' 预留空间
- 字符串长度是 n,但需要 n+1 个字节存 '\0'
🎯 面试重点总结:
浅拷贝:值拷贝,共用内存 → 多次释放崩溃
深拷贝:每个对象独立内存 → 安全但有开销
只要类里有指针成员,必须自己写拷贝构造和赋值重载!
(二)常用接口的实现
1. size () 和 capacity ()
简介作用
-
size() :返回字符串实际存储的字符个数(不包括 '\0')
-
capacity() :返回字符串最多能存多少个字符(容量)
就像一个水杯:
-
size = 杯子里现在有多少水
-
capacity = 杯子总共能装多少水
代码例子:size 和 capacity 的实现
例子说明:实现获取字符串大小和容量的接口
cpp
class string {
private:
char* _str;
int _size; // 实际字符数
int _capacity; // 容量
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 返回有效字符个数
int size() const {
return _size;
}
// ✅ 返回容量
int capacity() const {
return _capacity;
}
};
int main() {
string s("hello");
cout << "size = " << s.size() << endl; // 输出:5
cout << "capacity = " << s.capacity() << endl; // 输出:5
return 0;
}
2. operator[]
简介作用
让 string 对象能像数组一样用[]访问字符,支持读和写。
代码例子:operator [] 的实现
例子说明:实现下标访问运算符,支持 const 和非 const 版本
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 非const版本:支持读写
char& operator[](int pos) {
// 边界检查(断言,调试模式有效)
assert(pos >= 0 && pos < _size);
return _str[pos];
}
// ✅ const版本:只读(const对象调用)
const char& operator[](int pos) const {
assert(pos >= 0 && pos < _size);
return _str[pos];
}
};
int main() {
string s("hello");
// 读
cout << s[0] << endl; // h
// 写
s[0] = 'H';
cout << s[0] << endl; // H
// const对象只能读
const string s2("world");
cout << s2[0] << endl; // w,正确
// s2[0] = 'W'; // 编译错误!
return 0;
}
⚠️ 经典 Bug 和问题
-
越界访问:一定要加 assert 检查!
-
返回值类型 :必须返回引用
char&,否则无法修改 -
const 版本:const 对象只能调用 const 成员函数
3. c_str()
简介作用
返回 C 风格的字符串指针(const char*),用于和 C 语言接口兼容。
比如要调用printf、strcpy等 C 函数时,就需要用 c_str ()。
代码例子:c_str 的实现
例子说明:实现返回 C 风格字符串的接口
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 返回C风格字符串指针
const char* c_str() const {
return _str;
}
};
int main() {
string s("hello world");
// 和C函数兼容
printf("%s\n", s.c_str()); // 输出:hello world
// 用strlen计算长度
cout << strlen(s.c_str()) << endl; // 11
return 0;
}
4. reserve()
简介作用
扩容:提前申请更大的容量,避免频繁扩容影响性能。
注意:reserve 只扩不缩,如果 n 小于当前 capacity,什么都不做。
代码例子:reserve 的实现
例子说明:实现扩容接口,只扩大容量
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 扩容
void reserve(int n) {
// 只有n大于当前容量才扩容
if (n > _capacity) {
// 1. 申请新空间
char* tmp = new char[n + 1];
// 2. 拷贝旧数据
strcpy(tmp, _str);
// 3. 释放旧空间
delete[] _str;
// 4. 指向新空间
_str = tmp;
// 5. 更新容量
_capacity = n;
}
}
int capacity() const { return _capacity; }
int size() const { return _size; }
};
int main() {
string s("hello");
cout << "扩容前 capacity = " << s.capacity() << endl; // 5
s.reserve(100); // 扩容到100
cout << "扩容后 capacity = " << s.capacity() << endl; // 100
cout << "size不变 = " << s.size() << endl; // 5(size不变!)
s.reserve(50); // 50 < 100,什么都不做
cout << "再次扩容 capacity = " << s.capacity() << endl; // 还是100
return 0;
}
5. 构造函数
简介作用
创建 string 对象,初始化成员变量。我们需要实现几个常用的构造函数。
代码例子:各种构造函数的实现
例子说明:实现无参、带参、拷贝构造函数
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
// ✅ 1. 无参构造(空字符串)
string()
: _str(new char[1]{'\0'})
, _size(0)
, _capacity(0)
{}
// ✅ 2. 带C字符串构造
string(const char* str) {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 3. 拷贝构造(深拷贝)
string(const string& s) {
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
// 现代写法:利用swap(更安全)
// string(const string& s)
// : _str(nullptr), _size(0), _capacity(0)
// {
// string tmp(s._str); // 构造临时对象
// swap(tmp); // 交换
// }
};
int main() {
string s1; // 空字符串
string s2("hello"); // 带参构造
string s3 = s2; // 拷贝构造
return 0;
}
⚠️ 经典 Bug 和问题
-
空字符串的处理:无参构造也要给 '\0' 分配空间!
-
初始化列表顺序:成员变量声明顺序就是初始化顺序
-
现代写法更安全:先构造临时对象再 swap,异常安全
6. 析构函数
简介作用
对象销毁时释放内存,防止内存泄漏。
代码例子:析构函数的实现
例子说明:实现安全的析构函数
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 析构函数
~string() {
// 安全写法:先判断再释放
if (_str) {
delete[] _str;
_str = nullptr; // 置空,避免野指针
_size = 0;
_capacity = 0;
}
}
};
7. clear()
简介作用
清空字符串,把 size 置为 0,但 capacity 不变。
就像把杯子里的水倒掉,但杯子大小不变。
代码例子:clear 的实现
例子说明:实现清空字符串的接口
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "hello") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 清空字符串
void clear() {
_str[0] = '\0'; // 第一个字符设为结束符
_size = 0; // size置0
// capacity不变!
}
const char* c_str() const { return _str; }
int size() const { return _size; }
int capacity() const { return _capacity; }
};
int main() {
string s("hello");
cout << "清空前:" << s.c_str() << ", size=" << s.size() << endl;
s.clear();
cout << "清空后:" << s.c_str() << ", size=" << s.size() << endl;
cout << "capacity不变:" << s.capacity() << endl;
return 0;
}
运行结果:
cpp
清空前:hello, size=5
清空后:, size=0
capacity不变:5
8. 迭代器的简单模拟
简介作用
迭代器就是指针的封装,让我们可以用统一的方式遍历各种容器。
对于 string 来说,迭代器本质就是char*。
代码例子:迭代器的实现
例子说明:实现简单的迭代器,支持范围 for
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
// ✅ 迭代器类型重定义(本质就是char*)
typedef char* iterator;
typedef const char* const_iterator;
string(const char* str = "hello") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 返回第一个字符的迭代器
iterator begin() {
return _str;
}
const_iterator begin() const {
return _str;
}
// ✅ 返回最后一个字符下一个位置的迭代器
iterator end() {
return _str + _size;
}
const_iterator end() const {
return _str + _size;
}
};
int main() {
string s("hello");
// 方式1:迭代器遍历
string::iterator it = s.begin();
while (it != s.end()) {
cout << *it << " ";
++it;
}
cout << endl;
// 方式2:范围for(底层就是迭代器!)
for (char ch : s) {
cout << ch << " ";
}
cout << endl;
return 0;
}
运行结果:
cpp
h e l l o
h e l l o
💡 小知识:C++11 的范围 for 语法,编译器会自动转换成迭代器调用!
(三)其他接口的实现
1. push_back()
简介作用
在字符串末尾追加一个字符。
代码例子:push_back 的实现
例子说明:实现尾插一个字符的功能
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void reserve(int n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
// ✅ 尾插一个字符
void push_back(char ch) {
// 检查是否需要扩容
if (_size == _capacity) {
// 2倍扩容,空串特殊处理
int newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
_str[_size] = ch; // 插入字符
_size++; // size++
_str[_size] = '\0'; // 补'\0'!重要!
}
const char* c_str() const { return _str; }
};
int main() {
string s;
s.push_back('h');
s.push_back('e');
s.push_back('l');
s.push_back('l');
s.push_back('o');
cout << s.c_str() << endl; // hello
return 0;
}
⚠️ 经典 Bug 和问题
忘记补 '\0' ⭐⭐⭐⭐⭐
这是最容易犯的错误!插入字符后一定要在最后位置放 '\0',否则字符串就 "断" 了!
2. append()
简介作用
在字符串末尾追加一个字符串。
代码例子:append 的实现
例子说明:实现尾插字符串的功能
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void reserve(int n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
// ✅ 追加字符串
void append(const char* str) {
int len = strlen(str);
// 检查容量
if (_size + len > _capacity) {
reserve(_size + len);
}
// 从'\0'位置开始拷贝
strcpy(_str + _size, str);
_size += len;
}
const char* c_str() const { return _str; }
};
int main() {
string s("hello");
s.append(" world");
cout << s.c_str() << endl; // hello world
return 0;
}
3. operator+=
简介作用
重载+=运算符,支持追加字符或字符串,用起来更方便。
代码例子:operator+= 的实现
例子说明:实现 += 运算符重载,支持追加字符和字符串
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void reserve(int n) { /* 同上 */ }
void push_back(char ch) { /* 同上 */ }
void append(const char* str) { /* 同上 */ }
// ✅ += 字符
string& operator+=(char ch) {
push_back(ch);
return *this;
}
// ✅ += 字符串
string& operator+=(const char* str) {
append(str);
return *this;
}
// ✅ += string对象
string& operator+=(const string& s) {
append(s._str);
return *this;
}
const char* c_str() const { return _str; }
};
int main() {
string s("I");
s += " love"; // 加C字符串
s += ' '; // 加字符
s += string("C++"); // 加string对象
cout << s.c_str() << endl; // I love C++
return 0;
}
💡 设计技巧:复用已有的 push_back 和 append,代码不重复!
4. insert()
简介作用
在任意位置插入字符或字符串。
代码例子:insert 的实现
例子说明:实现在指定位置插入字符或字符串
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void reserve(int n) { /* 同上 */ }
// ✅ 在pos位置插入字符
string& insert(int pos, char ch) {
assert(pos >= 0 && pos <= _size);
// 检查扩容
if (_size == _capacity) {
int newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
// 挪动数据:从后往前挪
int end = _size;
while (end >= pos) {
_str[end + 1] = _str[end];
end--;
}
// 插入
_str[pos] = ch;
_size++;
return *this;
}
// ✅ 在pos位置插入字符串
string& insert(int pos, const char* str) {
assert(pos >= 0 && pos <= _size);
int len = strlen(str);
if (_size + len > _capacity) {
reserve(_size + len);
}
// 挪动数据
int end = _size;
while (end >= pos) {
_str[end + len] = _str[end];
end--;
}
// 拷贝插入
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
const char* c_str() const { return _str; }
};
int main() {
string s("world");
s.insert(0, "hello "); // 在开头插入
cout << s.c_str() << endl; // hello world
s.insert(5, ','); // 在第5位插入逗号
cout << s.c_str() << endl; // hello, world
return 0;
}
⚠️ 经典 Bug 和问题
挪动数据方向搞反 ⭐⭐⭐⭐
插入时必须从后往前挪数据!如果从前往后挪,数据会被覆盖!
5. erase()
简介作用
从指定位置开始删除 n 个字符。
代码例子:erase 的实现
例子说明:实现删除指定位置字符的功能
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
// 静态成员:表示"直到结尾"
static const int npos;
public:
string(const char* str = "hello world") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 删除:从pos开始删len个
string& erase(int pos, int len = npos) {
assert(pos >= 0 && pos < _size);
// 1. 如果删到末尾,或len太大,直接删到结尾
if (len == npos || pos + len >= _size) {
_str[pos] = '\0';
_size = pos;
}
// 2. 删中间部分:往前覆盖
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
const char* c_str() const { return _str; }
};
// 静态成员初始化
const int string::npos = -1;
int main() {
string s("hello world");
s.erase(5, 1); // 从第5位删1个(空格)
cout << s.c_str() << endl; // helloworld
s.erase(5); // 从第5位删到末尾
cout << s.c_str() << endl; // hello
return 0;
}
6. find()
简介作用
查找字符或字符串第一次出现的位置,找到返回下标,找不到返回 npos。
代码例子:find 的实现
例子说明:实现查找字符和字符串的功能
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
static const int npos;
public:
string(const char* str = "hello world") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 查找字符
int find(char ch, int pos = 0) const {
assert(pos >= 0 && pos < _size);
for (int i = pos; i < _size; i++) {
if (_str[i] == ch) {
return i;
}
}
return npos;
}
// ✅ 查找字符串(BF算法)
int find(const char* str, int pos = 0) const {
assert(pos >= 0 && pos < _size);
const char* p = strstr(_str + pos, str);
if (p == nullptr) {
return npos;
}
return p - _str; // 指针相减得到下标
}
};
const int string::npos = -1;
int main() {
string s("hello world");
cout << s.find('w') << endl; // 6
cout << s.find("world") << endl; // 6
cout << s.find('x') << endl; // -1(npos)
if (s.find("hello") != string::npos) {
cout << "找到了!" << endl;
}
return 0;
}
7. substr()
简介作用
截取子串:从 pos 开始截取 len 个字符,返回新的 string。
代码例子:substr 的实现
例子说明:实现截取子串的功能
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
static const int npos;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// ✅ 截取子串
string substr(int pos = 0, int len = npos) const {
assert(pos >= 0 && pos < _size);
int realLen = len;
// 计算实际截取长度
if (len == npos || pos + len > _size) {
realLen = _size - pos;
}
// 临时数组存子串
char* tmp = new char[realLen + 1];
strncpy(tmp, _str + pos, realLen);
tmp[realLen] = '\0';
string sub(tmp); // 构造子串对象
delete[] tmp; // 释放临时内存
return sub;
}
const char* c_str() const { return _str; }
};
const int string::npos = -1;
int main() {
string s("hello world");
string sub1 = s.substr(0, 5);
cout << sub1.c_str() << endl; // hello
string sub2 = s.substr(6);
cout << sub2.c_str() << endl; // world
return 0;
}
8. 六个比较运算符
简介作用
实现字符串的大小比较,按 ASCII 码字典序比较。
代码例子:比较运算符的实现
例子说明:实现 <<= ==> >= != 六个运算符
cpp
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
const char* c_str() const { return _str; }
};
// ✅ < 运算符
bool operator<(const string& s1, const string& s2) {
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
// ✅ == 运算符
bool operator==(const string& s1, const string& s2) {
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
// ✅ <= (复用上面两个)
bool operator<=(const string& s1, const string& s2) {
return s1 < s2 || s1 == s2;
}
// ✅ >
bool operator>(const string& s1, const string& s2) {
return !(s1 <= s2);
}
// ✅ >=
bool operator>=(const string& s1, const string& s2) {
return !(s1 < s2);
}
// ✅ !=
bool operator!=(const string& s1, const string& s2) {
return !(s1 == s2);
}
int main() {
string s1("apple");
string s2("banana");
cout << boolalpha;
cout << "apple < banana: " << (s1 < s2) << endl; // true
cout << "apple == apple: " << (s1 == s1) << endl; // true
cout << "apple > banana: " << (s1 > s2) << endl; // false
return 0;
}
💡 设计技巧 :只需要实现
<和==,其他 4 个都可以复用!
9. 流输入 流提取
简介作用
让 string 支持cout输出和cin输入,用起来和内置类型一样方便。
代码例子:流运算符的实现
例子说明:实现 <<和>> 运算符重载
cpp
#include <iostream>
using namespace std;
class string {
private:
char* _str;
int _size;
int _capacity;
public:
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void reserve(int n) { /* 同上 */ }
void push_back(char ch) { /* 同上 */ }
void clear() { _str[0] = '\0'; _size = 0; }
const char* c_str() const { return _str; }
int size() const { return _size; }
char& operator[](int pos) { return _str[pos]; }
};
// ✅ 流输出 <<
ostream& operator<<(ostream& out, const string& s) {
for (int i = 0; i < s.size(); i++) {
out << s[i];
}
return out;
}
// ✅ 流输入 >>
istream& operator>>(istream& in, string& s) {
s.clear(); // 先清空原有内容
char ch;
ch = in.get();
// 跳过前面的空格、换行
while (ch == ' ' || ch == '\n') {
ch = in.get();
}
// 读取字符,遇到空格/换行停止
while (ch != ' ' && ch != '\n') {
s.push_back(ch);
ch = in.get();
}
return in;
}
int main() {
string s;
cout << "请输入一个字符串:";
cin >> s;
cout << "你输入的是:" << s << endl;
return 0;
}
🎯 面试重点总结
必背考点(90% 概率考)
-
⭐⭐⭐⭐⭐ 深浅拷贝的区别
-
浅拷贝:共用内存,多次释放崩溃
-
深拷贝:独立内存,安全但有开销
-
-
⭐⭐⭐⭐ 拷贝构造和赋值重载
-
只要类有指针成员,必须自己实现
-
注意自赋值检查
-
-
⭐⭐⭐ 扩容机制
-
一般 2 倍扩容
-
reserve 只扩不缩
-
-
⭐⭐⭐ 迭代器本质
- string 迭代器就是 char*
常见坑点提醒
-
❌ 忘记给 '\0' 预留空间
-
❌ push_back 后忘记补 '\0'
-
❌ insert 时数据挪动方向搞反
-
❌ 赋值重载忘记检查自赋值
-
❌ 析构函数重复释放
总结啦~
恭喜你!看完这篇文章,你已经掌握了 string 的模拟实现!
string 是 C++ 最重要的容器之一,它的实现涵盖了:
-
类的设计思想
-
内存管理(深浅拷贝)
-
运算符重载
-
迭代器设计
这些都是 C++ 的核心知识点!
下一篇预告:我们将进入 vector 的学习啦,敬请期待哦!
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注我! 有问题欢迎在评论区留言,我会一一回复!