六、string类
5. string类的操作
上面的函数中,有些是不常用的,咱们只挑几个重要的进行讲解。
c_str 就是将字符串转换成 C语言 字符串的格式。
get_allocator 就是申请内存池,目前用不上。
copy 就是拷贝,缺点是必须先创建好一个字符数组,字符数组还是形参,返回拷贝的个数,被下面的 substr 函数完虐。substr 函数返回一个 string 类型。
假设有个情景:需要我们找出文件名的后缀,该怎么办?
#include<iostream> #include<string> using namespace std; void test1() { string s("file.cpp"); // find函数返回第一个出现的下标 size_t pos = s.find('.'); // 在string类里,没找到都会以npos的形式返回。 if (pos != string::npos) { string suffix = s.substr(pos); cout << suffix << endl; } } int main() { test1(); return 0; }
find 函数
substr 函数
那么假设这个文件里 . 很多,我们要返回最后一个后缀,该怎么办?这个时候,就到了 rfind 大展身手的时候了,rfind 函数也是查找,不过是从后往前查找。
#include<iostream> #include<string> using namespace std; void test2() { string s("file.cpp.rar.zip.tar"); size_t pos = s.rfind('.'); if (pos != string::npos) { string suffix = s.substr(pos); cout << suffix << endl; } } int main() { test2(); return 0; }
再假设一个情景,有一个网址,将其 协议 , 域名 , 资源名 给分离开,怎么搞呢?
#include<iostream> #include<string> using namespace std; void test3() { string url("https://cplusplus.com/reference/string/string/substr/"); // 协议 string protocol; // 域名 string domain; // 资源名 string uri; size_t i1 = url.find(":"); if (i1 != string::npos) { protocol = url.substr(0, i1 - 0); cout << protocol << endl; } // 从域名开始找 '/' size_t i2 = url.find('/', i1 + 3); if (i2 != string::npos) { domain = url.substr(i1 + 3, i2 - i1 - 3); cout << domain << endl; // 剩下的都是资源名 uri = url.substr(i2 + 1); cout << uri << endl; } } int main() { test3(); return 0; }
compare 就是比较,不过 string 针对比较都做了重载,可用直接用 > ! = 等比较。
6. string类的转换
当我们涉及到其他类型转换成字符串类型时,有这样一个函数:
#include<iostream> #include<string> using namespace std; void test4() { int a = 123456; string s = to_string(a); cout << s << endl; } int main() { test4(); return 0; }
当然,有其他类型转换成字符串类型,就有字符串类型转换成其他类型。
#include<iostream> #include<string> using namespace std; void test5() { string s1("13579"); string s2("3.1415926"); int i = stoi(s1); double d = stod(s2); cout << i << endl << d << endl; } int main() { test5(); return 0; }
7. string类的模拟实现
接下来我们来具体实现一下 string 类。在实现 string 类的时候,我们需要将其封装一下,包含在自己的命名空间下。
// string.h 头文件下 #pragma once #include<iostream> // 我们自己实现的 string 类的专属命名空间 namespace my { class string { public: // private: // 字符数组 存数据 char* _str; // 无符号整形 存数据个数、字符串的长度 size_t _size; // 存为该字符串已经分配的空间、容量 size_t _capacity; }; }
接下来我们来实现构造函数,分为无参构造函数,和有参构造函数。
// my 命名空间下 class string { public: // 无参构造函数 string() :_str(nullptr) // 初始化列表 ,_size(0) ,_capacity(0) {} // 有参构造函数 string(const char* str) :_size(strlen(str)) { _capacity = _size; _str = new char[_capacity + 1]; strcpy(_str, str); } private: char* _str; size_t _size; size_t _capacity; };
构造函数不一定非要使用初始化列表,我们在有参构造函数里面若是使用初始化列表,由于三个对象都需要调用 strlen 函数,导致效率不高,所以可以初始化列表和赋值一起使用。