目录
一、标准库中的string类
🍔string类官方介绍
二、string类的常用接口
🍔string类的构造
🍟string类的容量操作
🌮string类的访问及遍历
🥪string类的修改操作
🧀string类非成员函数
三、string类的模拟实现
🍔string类的经典问题
🍟浅拷贝
🌮深拷贝
🥪正确(深拷贝)实现
四、结语
一、标准库中的string类
🍔string类官方介绍
🌟以下是string类的文档介绍:
💻官方网址:https://legacy.cplusplus.com/reference/string/string/?kw=string
1.String是表示字符序列的类
2.标准string类通过类似于标准字节容器的接口提供了对此类对象的支持,但添加了专门设计用于操作单字节字符串的特性。
3.string类是basic_string类模板的实例化,该模板使用char(即字节)作为其字符类型,具有默认的char_traits和allocator类型(有关模板的更多信息,请参阅basic_string)。
4.请注意,该类处理字节独立于所使用的编码:如果用于处理多字节或变长字符序列(如UTF-8),则该类的所有成员(如length或size)及其迭代器仍将以字节(而不是实际编码的字符)进行操作。
⬇️总结来说:1.string是表示字符串的类
2.改类的接口与常规的接口基本相同,但添加了一些专门来操作string的操作
3.string在底层实际是:basic_string模板类的别名
typedef basic_string<char,char_traits,allocator>
4.不能操作多字节或变长字符序列
二、string类的常用接口
以下只介绍常见的功能,了解如何使用即可,重点在 “string类的模拟实现”
大家也可查看官方文档了解:https://legacy.cplusplus.com/reference/string/string/
🍔string类的构造
🔥string类对象的常见构造及功能:
💧使用演示:
void test_1() {string s1; //构造空的string类对象 string s2("hello worle"); //用c格式字符串构造string类对象string s3(s2); //拷贝构造string s4(5, '*'); //5个'*'构造string类对象 }
🍟string类的容量操作
🔥string类对象的容量操作:
❗使用时注意:
1️⃣size( )和length( )底层实现原理完全相同,引入size( )是为了和其他容器的接口名保持一致,一般情况下基本使用size( )
2️⃣clear( )只清空有效字符,不改变空间大小
3️⃣reserve(size_t n=0) 为string 提前预留好空间,若 n 比底层空间小时,reserve不会改变容量大小
4️⃣resize (size_t n) resize (size_t n, char c) 都可以使元素个数增多或者减小,不同的是当字符个数增多时,resize(n)用0来填充,resize(n,'c')用字符'c'来填充
🌮string类的访问及遍历
🔥string类对象的访问和遍历:
🥪string类的修改操作
🔥string类对象修改操作:
🧀string类非成员函数
🔥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 test_2() {String s1("hello world");String s2(s1); }
🔥其中构造函数给缺省参数是重点,因为实际中是空字符:
程序运行后,在free时报错:
🌟原因分析:对野指针进行操作 !
🍟浅拷贝
📕浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。
如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
🌮深拷贝
📕深拷贝:每个对象都有一份独立的资源,不要和其他对象共享
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
🥪正确(深拷贝)实现
🌟我们用两种方式来实现,第二种方法更为巧妙:
🏇下面是传统方式实现:
class String { public:String(const char* str = ""){if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}String(const String& s):_str(new char[strlen(s._str)+1]){strcpy(_str, s._str);}String& operator=(const String& s){if (this != &s){char* pStr = new char[strlen(s._str) + 1];strcpy(pStr, s._str);delete[] _str;_str = pStr;}}~String(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str; };
🚗下面是现代方式:
拷贝构造函数现代实现:
1️⃣用构造函数创建一个strTmp对象
2️⃣交换_str完成拷贝构造
赋值运算符重载现代实现:
1️⃣传参时,直接用传值传参方式,传值传参会创建一个临时对象s
2️⃣交换_str
3️⃣同时函数结束时,会调用s的析构函数,还能顺带将原this的_str释放
四、结语
string类的完整代码复现见另一篇博客:
https://blog.csdn.net/2301_79825793/article/details/144332978?sharetype=blogdetail&sharerId=144332978&sharerefer=PC&sharesource=2301_79825793&sharefrom=mp_from_link
🫡你的点赞和关注是作者前进的动力!
🌞最后,作者主页有许多有趣的知识,欢迎大家关注作者,作者会持续更新有意思的代码,在有趣的玩意儿中成长!