《string 类模拟实现(收尾):传统与现代写法对比及底层机制探析》

🔥个人主页:K 旺仔小馒头
🍉学习方向:C/C++方向学习者
📖个人专栏:《C语言》《数据结构与算法》《C++知识分享》《C语言实战编程》《算法题从优选到贪心的解题技巧》
⭐️人生格言:“何时葡萄先熟透,你要静候再静候”

目录
前言:
一. string的深拷贝:
扩展阅读:
二. 库里面string的底层涉及到一些小小问题
2.1 vs系列:(例如:小明打针,挂水 住院)
2.2 g++系列:(例如:小明开药,观察)
三. 写时拷贝(了解)
四. 浅拷贝
五. 深拷贝
六. 接口:
结尾:
前言:
C++ 的 string 类底层实现藏着诸多细节,本文聚焦深拷贝的两种实现方式:传统写法通过内存分配与数据复制完成操作,现代写法则借临时对象与 swap 函数简化逻辑。我们将对比二者差异,解析浅拷贝风险、深拷贝必要性及写时拷贝机制,助你吃透字符串核心原理。
一. string的深拷贝:
知识点:
1. 传统写法和现代写法本质上它们的效率没有区别,只是写法上的区别而已
2. delete和free都可以对空进行释放,不会报错的
string.h
public:string(const string& s);//拷贝构造//string& operator=(const string& s);//赋值重载string& operator=(string s);//赋值重载private://这些变量可以给缺省值char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;
string.cpp
//传统写法
//string s2(s1);
//string::string(const string& s) //拷贝构造
//{
// _str = new char[s._capacity + 1];
// //strcpy(_str, s._str);
// memcpy(_str, s._str, s._size + 1);
// _size = s._size;
// _capacity = s._capacity;
//}//s1 = s3;
//string& string::operator=(const string& s) //赋值重载
//{
// if (this != &s)
// {
// char* tmp = new char[s._capacity + 1];
// //strcpy(tmp, s._str);
// memcpy(tmp, s._str, s._size + 1);
// delete[] _str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// }
//
// return *this;
//}//现代写法->本质效率没有提升,只是写法不同,更简洁,是另一种思路(代码复用)
string::string(const string& s) //拷贝构造
{string tmp(s._str);swap(tmp);
}//string& string::operator=(const string& s) //赋值重载
//{
// if (this != &s)
// {
// string tmp(s._str);
// swap(tmp);
// }// return *this;
//}string& string::operator=(string tmp) //赋值重载还可以更简洁一些
{swap(tmp);return *this;
}
代码测试:
Test.cpp
namespace bit
{void test_string9(){string s1("hello world");cout << s1 << endl;string s2(s1);cout << s2 << endl;string s3("xxxxxxxxxxxxxxxx");s1 = s3;cout << s1 << endl;cout << s3 << endl;}
}int main()
{try{bit::test_string9();//cout << typeid(bit::string::iterator).name() << endl;//打印出真实的类型//cout << typeid(std::string::iterator).name() << endl;}catch (const exception& e){cout << e.what() << endl;}return 0;
}
大家想写哪种写法(传统写法和现代写法)都可以
扩展阅读:
1. 面试中string的一种正确写法
2. STL中的string类怎么了?
二. 库里面string的底层涉及到一些小小问题
string.h
public:private://char _buff[16];//SBO小对象优化//这些变量可以给缺省值char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;
代码测试:
Test.cpp
int main()
{try{std::string s1;std::string s2("hello world");std::string s3("hello worldxxxxxxxxxxxxxxxxxxxxxxx");cout << sizeof(s1) << endl;cout << sizeof(s2) << endl;}catch (const exception& e){cout << e.what() << endl;}return 0;
}
vs和g++下string结构的说明:
注意:32位平台下指针占4个字节
2.1 vs系列:(例如:小明打针,挂水 住院)
2.2 g++系列:(例如:小明开药,观察)
g++是gcc的一部分
引用计数这么好,为什么新一点的不用(引用计数)这个方案了呢?
原因:
1. 多线程情况,维护引用计数的线程安全有一定代价
2. 动态库下,也会存在一些问题
3. C++11以后,编译器各种优化,传值返回代价很低,有了移动构造,移动赋值。
三. 写时拷贝(了解)
写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
引用计数:用来记录资源使用者的个数(引用计数是几就代表有几个对象指向这块空间)。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。
写时拷贝
写时拷贝在读取时的缺陷
四. 浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一 不想分享就你争我夺,玩具损坏。
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。
五. 深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
给每个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放造成程序奔溃问题。
六. 接口:
整型和字符串之间的转换,以后就用这个了(方便)
1. 任何类型(整型、浮点数)转换成字符串(string对象)用to_string
to_string是写在string头文件里面的,是全局函数
2. 把字符串转换成整型,调用stoi,依次类推....
结尾:
往期精选:
《string 类模拟实现(上):从构造析构到插入操作的逐步实现》
《string 类模拟实现(下):补全核心功能,吃透字符串操作底层逻辑》
《优选算法:01 双指针巧解移动零问题》
结语:通过解析 string 类深拷贝的传统与现代实现,我们明晰了底层逻辑差异:效率相近时,现代写法更显简洁。浅拷贝的隐患、写时拷贝的局限,以及类型转换接口,共同构成了 string 实现的关键。愿这些解析能帮你更深入理解字符串操作,提升底层编程能力。


