跳到主要内容
C++ string 类模拟实现详解 | 极客日志
C++ 算法
C++ string 类模拟实现详解 C++ string 类模拟实现主要解决浅拷贝导致的内存重复释放问题,通过深拷贝确保资源独立。内容涵盖构造函数、析构函数、赋值运算符重载及常用成员函数如 push_back、append、insert 等的具体逻辑。重点展示了如何管理动态内存、处理扩容策略以及实现比较与流操作符,为理解 C++ 标准库底层机制提供参考。
beaabea 发布于 2025/12/9 更新于 2026/9/4 59 浏览C++ string 类模拟实现
1.1 经典的 string 类问题
在面试中,面试官常要求模拟实现 string 类,核心在于构造、拷贝构造、赋值运算符重载及析构函数。以下 String 类实现存在潜在问题:
struct 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) ;
}
上述 String 类没有显式定义其拷贝构造函数与赋值运算符重载,编译器会合成默认的。当用 s1 构造 s2 时,调用默认拷贝构造,导致 s1、s2 共用同一块内存空间。释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式称为浅拷贝。
1.2 浅拷贝
浅拷贝 :也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源。当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规。
可以采用深拷贝解决浅拷贝问题,即每个对象都有一份独立的资源,不要和其他对象共享。
1.3 深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
模拟实现
构造函数的模拟实现
string (const string& s) {
string tmp (s._str) ;
swap (tmp);
}
赋值重载的模拟实现
string& operator =(string tmp) {
swap (tmp);
return *this ;
}
析构的模拟实现 ~string () {
delete [] _str;
_str = nullptr ;
_capacity = _size = 0 ;
}
clear 的模拟实现 void clear () {
_str[0 ] = '\0' ;
_size = 0 ;
}
reserve 的模拟实现 void string::reserve (size_t n) {
if (n > _capacity) {
char * tmp = new char [n + 1 ];
strcpy (tmp, _str);
delete [] _str;
_str = tmp;
_capacity = n;
}
}
push_back 的模拟实现
void string::push_back (char ch) {
if (_size == _capacity) {
reserve (_capacity == 0 ? 4 : _capacity * 2 );
}
_str[_size] = ch;
++_size;
_str[_size] = '\0' ;
}
append 的模拟实现 void string::append (const char * str) {
size_t len = strlen (str);
if (_size + len > _capacity) {
reserve (_size + len > _capacity * 2 ? _size + len : _capacity * 2 );
}
strcpy (_str + _size, str);
_size += len;
}
insert 的模拟实现 void string::insert (size_t pos, char ch) {
assert (pos <= _size);
if (_size == _capacity) {
reserve (_capacity == 0 ? 4 : _capacity * 2 );
}
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 > _capacity * 2 ? _size + len : _capacity * 2 );
}
size_t end = _size + len;
if (len == 0 ) return ;
while (end > pos + len - 1 ) {
_str[end] = _str[end - len];
--end;
}
for (size_t i = 0 ; i < len; ++i) {
_str[pos + i] = str[i];
}
_size += len;
}
erase 的模拟实现 void string::erase (size_t pos, size_t len) {
assert (pos < _size);
if (len > _size - pos) {
_str[pos] = '\0' ;
_size = pos;
} else {
for (size_t i = pos + len; i <= _size; i++) {
_str[i - len] = _str[i];
}
_size -= len;
}
}
find 的模拟实现 size_t string::find (char ch, size_t pos) {
for (size_t i = pos; i < _size; i++) {
if (_str[i] == ch) return i;
}
return npos;
}
size_t string::find (const char * str, size_t pos) {
assert (pos < _size);
const char * ptr = strstr (_str + pos, str);
if (ptr == nullptr ) {
return npos;
} else {
return ptr - _str;
}
}
substr 的模拟实现 string string::substr (size_t pos, size_t len) {
assert (pos < _size);
if (len > _size - pos) {
len = _size - pos;
}
string sub;
sub.reserve (len);
for (size_t i = 0 ; i < len; ++i) {
sub += _str[pos + i];
}
return sub;
}
比较运算符的模拟实现 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 || s1 == s2;
}
bool operator >=(const string& s1, const string& s2) {
return !(s1 < s2);
}
bool operator !=(const string& s1, const string& s2) {
return !(s1 == s2);
}
流插入的模拟实现 ostream& operator <<(ostream& out, const string& s) {
for (auto ch : s) {
out << ch;
}
return out;
}
流提取的模拟实现 istream& operator >>(istream& in, string& s) {
s.clear ();
const int N = 256 ;
char buff[N];
int i = 0 ;
char ch;
ch = in.get ();
while (ch != ' ' && ch != '\n' ) {
buff[i++] = ch;
if (i == N - 1 ) {
buff[i] = '\0' ;
s += buff;
i = 0 ;
}
s += ch;
ch = in.get ();
if (i > 0 ) {
buff[i] = '\0' ;
s += buff;
}
}
return in;
}
完整代码
string.cpp #include "string.h"
namespace bit {
void string::reserve (size_t n) {
if (n > _capacity) {
char * tmp = new char [n + 1 ];
strcpy (tmp, _str);
delete [] _str;
_str = tmp;
_capacity = n;
}
}
void string::push_back (char ch) {
if (_size == _capacity) {
reserve (_capacity == 0 ? 4 : _capacity * 2 );
}
_str[_size] = ch;
++_size;
_str[_size] = '\0' ;
}
string& string::operator +=(char ch) {
push_back (ch);
return *this ;
}
void string::append (const char * str) {
size_t len = strlen (str);
if (_size + len > _capacity) {
reserve (_size + len > _capacity * 2 ? _size + len : _capacity * 2 );
}
strcpy (_str + _size, str);
_size += len;
}
string& string::operator +=(const char * str) {
append (str);
return *this ;
}
void string::insert (size_t pos, char ch) {
assert (pos <= _size);
if (_size == _capacity) {
reserve (_capacity == 0 ? 4 : _capacity * 2 );
}
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 > _capacity * 2 ? _size + len : _capacity * 2 );
}
size_t end = _size + len;
if (len == 0 ) return ;
while (end > pos + len - 1 ) {
_str[end] = _str[end - len];
--end;
}
for (size_t i = 0 ; i < len; ++i) {
_str[pos + i] = str[i];
}
_size += len;
}
void string::erase (size_t pos, size_t len) {
assert (pos < _size);
if (len > _size - pos) {
_str[pos] = '\0' ;
_size = pos;
} else {
for (size_t i = pos + len; i <= _size; i++) {
_str[i - len] = _str[i];
}
_size -= len;
}
}
size_t string::find (char ch, size_t pos) {
for (size_t i = pos; i < _size; i++) {
if (_str[i] == ch) return i;
}
return npos;
}
size_t string::find (const char * str, size_t pos) {
assert (pos < _size);
const char * ptr = strstr (_str + pos, str);
if (ptr == nullptr ) {
return npos;
} else {
return ptr - _str;
}
}
string string::substr (size_t pos, size_t len) {
assert (pos < _size);
if (len > _size - pos) {
len = _size - pos;
}
string sub;
sub.reserve (len);
for (size_t i = 0 ; i < len; ++i) {
sub += _str[pos + i];
}
return sub;
}
void test_string1 () {
string s1;
string s2 ("hello world" ) ;
cout << s1. c_str () << endl;
cout << s2. c_str () << endl;
for (size_t i = 0 ; i < s2. size (); ++i) {
s2[i] += 2 ;
}
cout << s2. c_str () << endl;
for (auto e : s2) {
cout << e << " " ;
}
cout << endl;
string::iterator it = s2. begin ();
while (it != s2. end ()) {
cout << *it << " " ;
++it;
}
}
void test_string2 () {
string s1 ("hello wwww" ) ;
s1 += 'x' ;
cout << s1. c_str () << endl;
s1. insert (3 , "sss" );
cout << s1. c_str () << endl;
s1. erase (3 , 2 );
cout << s1. c_str () << endl;
}
void test_string3 () {
string s ("666.cpp.zip" ) ;
size_t pos = s.find ('.' );
string suffix = s.substr (pos);
cout << suffix.c_str () << endl;
string copy (s) ;
cout << copy.c_str () << endl;
}
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 || s1 == s2;
}
bool operator >=(const string& s1, const string& s2) {
return !(s1 < s2);
}
bool operator !=(const string& s1, const string& s2) {
return !(s1 == s2);
}
ostream& operator <<(ostream& out, const string& s) {
for (auto ch : s) {
out << ch;
}
return out;
}
istream& operator >>(istream& in, string& s) {
s.clear ();
const int N = 256 ;
char buff[N];
int i = 0 ;
char ch;
ch = in.get ();
while (ch != ' ' && ch != '\n' ) {
buff[i++] = ch;
if (i == N - 1 ) {
buff[i] = '\0' ;
s += buff;
i = 0 ;
}
s += ch;
ch = in.get ();
if (i > 0 ) {
buff[i] = '\0' ;
s += buff;
}
}
return in;
}
void test_string4 () {
string s1 ("hello" ) ;
cin >> s1;
cout << s1 << endl;
cout << "he" ;
}
int main () {
bit::test_string4 ();
}
}
string.h #pragma once
#include <iostream>
#include <assert.h>
#include <string.h>
using namespace std;
namespace bit {
class string {
public :
typedef char * iterator;
iterator begin () {
return _str;
}
iterator end () {
return _str + _size;
}
typedef const char * const_iterator;
const_iterator begin () const {
return _str;
}
const_iterator end () const {
return _str + _size;
}
size_t capacity () const {
return _capacity;
}
void reserve (size_t n) ;
void push_back (char ch) ;
void append (const char * str) ;
string& operator +=(char ch);
string& operator +=(const char * str);
void insert (size_t pos, char ch) ;
void insert (size_t pos, const char * str) ;
void erase (size_t pos, size_t len = npos) ;
size_t find (char ch, size_t pos = 0 ) ;
size_t find (const char * str, size_t pos = 0 ) ;
string substr (size_t pos = 0 , size_t len = npos) ;
string (const char * str = "" ) {
_size = strlen (str);
_capacity = _size;
_str = new char [_capacity + 1 ];
strcpy (_str, str);
}
string (const string& s) {
string tmp (s._str) ;
swap (tmp);
}
string& operator =(string tmp) {
swap (tmp);
return *this ;
}
void swap (string& s) {
std::swap (_str, s._str);
std::swap (_size, s._size);
std::swap (_capacity, s._capacity);
}
~string () {
delete [] _str;
_str = nullptr ;
_capacity = _size = 0 ;
}
void clear () {
_str[0 ] = '\0' ;
_size = 0 ;
}
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];
}
private :
char * _str = nullptr ;
size_t _size = 0 ;
size_t _capacity = 0 ;
static const size_t npos = -1 ;
};
}
相关免费在线工具 加密/解密文本 使用加密算法(如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