赞
踩
从语法的角度来讲string是为了改进c语言字符串而创造的一个类,它本质上其实就是一个字符串,不过可以进行许多c语言字符串没有的操作。
字符串是表示字符序列的类
标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
单字节字符字符串的设计特性。
息,请参阅basic_string)。
和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
string是表示字符串的字符串类
该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
string;
在使用string类时,必须包含#include头文件以及using namespace std;
(constructor)函数名称 | 功能说明 |
---|---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
以下为代码实操以及结果:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string a;
char b[] = "Hello";
string c(b);
string d(5, 'a');
string e(d);
}
结果图:
函数名称 | 功能说明 |
---|---|
size | 返回字符串有效字符长度//不算’/0’ |
length | 返回字符串有效字符长度//不算’/0’ |
capacity | 返回空间总大小 |
empty | 检测字符串释放为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 强制 vector 容器的容量至少为 n。 |
resize | 将有效字符的个数改成n个 |
tips:
致,一般情况下基本都是用size()。
clear()只是将string中有效字符清空,不改变底层空间大小。
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
小,如果是将元素个数减少,底层空间总大小不变。
下面是实际代码用法:
#include<iostream> #include<string> using namespace std; int main() { string test = "abcdefg"; int a = test.size(); int b = test.length(); int c = test.capacity(); bool judge = test.empty(); test.clear(); test.reserve(20); test.resize(20, 'a'); }
函数名称 | 功能说明 |
---|---|
operator[] | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器+ end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | rbegin指向容器c的最后一个元素,rend指向容器c的第一个元素前面的位置 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
(1)string类中重要的操作符重载使用及其底层代码
赋值符号的重载
#include<iostream> #include<string> using namespace std; template<class T> void swap(T& a, T& b) { T temp = a; a = b; b = temp; } namespace own { class string { public: //旧版赋值运算符重载,本质开空间 /*string& operator= (const string& s) { if (this != &s) { char* temp = new char[strlen(s._str) + 1]; strcpy(temp, _str); delete[]_str; _str = temp; } return *this; }*/ //新版本赋值运算符重载,本质换地址 string& operator= (const string& s) { string temp(s); swap(_str, temp._str); } return *this; private: char* _str; }; }
深拷贝的复现
首先我们知道拷贝构造在不重写的情况下进行的是浅拷贝,用的是同一块空间。而这里我们将复现一下深拷贝,其原理和赋值运算符重载有一定相似,便放在这块进行类比。
class string { //传统写法 /*string(const string& s) :_str(new char [stelen(s._str)+1]) { strcpy(_str, s._str); }*/ //现代写法(其实本质上就是换地址) string(const string& s) :_str(nullptr) { string temp(s._str);//注意这里得是深拷贝 swap(_str, temp._str); } private: char* _str };
string类的基础操作,增删查改以及一些常规的函数模拟
class string { public: typedef char* iterator;//迭代器本质 typedef const char* const_iterator; const_iterator begin() const { return _str; } const_iterator end() const { return _str + _size; } iterator begin() { return _str; } iterator end() { return _str + _size; }//迭代器中begin的返回 //构造函数 string(const char* str = "") :_size(strlen(str)) ,_capacity(_size) { _str = new char[_capacity + 1]; strcpy(_str, str); } //交换 void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } //拷贝构造,这边为什么在写一遍主要为了强调下面这个swap string(const string& s) :_str(nullptr) , _size(0) , _capacity(0) { string tmp(s._str); //this->swap(tmp); swap(tmp);//这样换消耗小,如果按照上面写的那样换的话会进行3次深拷贝。 } //析构函数(进行了内存动态管理) ~string() { delete[]_str; _str = nullptr; size = capacity = 0; } //[]的运算符重载 char& operator[](size_t pos) { assert(pos <= _size); return _str[pos]; } //reserve的复刻 void reserve(size_t n) { if (n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } //resize的复刻 void resize(size_t n, char ch = '\0') { if (n <= _size) { _size = n; _str[_size] = '\0'; } else { if (n > _capacity) { reserve(n); } memset(_str + _size, ch, n - _size); _size = n; _str[_size] = '\0'; } } //尾插的三种方式 void push_back(char ch) { /*if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } _str[_size] = ch; ++_size; _str[_size] = '\0';*/ insert(_size, ch); } void append(const char* str) { /*size_t len = strlen(str); if (_size + len > _capacity) { reserve(_size + len); } strcpy(_str + _size, str); _size += len;*/ insert(_size, str); } string& operator+=(char ch) { push_back(ch); return *this; } string& operator+=(const char* str) { append(str); return *this; } //find复刻 size_t find(char ch) { for (size_t i = 0; i < _size; ++i) { if (ch == _str[i]) { return i; } } return npos; } size_t find(const char* s, size_t pos = 0) { const char* ptr = strstr(_str + pos, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } } //insert复刻 string& insert(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } /*size_t end = _size; while (end >= pos) { _str[end + 1] = _str[end]; --end; }*/ /*int end = _size; while (end >= (int)pos) { _str[end + 1] = _str[end]; --end; }*/ size_t end = _size + 1; while (end > pos) { _str[end] = _str[end - 1]; --end; } _str[pos] = ch; ++_size; return *this; } string& insert(size_t pos, const char* s) { assert(pos <= _size); size_t len = strlen(s); if (_size + len > _capacity) { reserve(_size + len); } size_t end = _size + len; while (end > pos) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, s, len); return *this; } //erase复刻 string& erase(size_t pos = 0, size_t len = npos) { assert(pos < _size); if (len == npos || pos + len >= _size) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } return *this; } private: char* _str; size_t _size;// 能存储有效字符的空间数,不包含\0 size_t _capacity; //实际空间大小 static const size_t npos; }; const size_t string::npos = -1;
迭代器的使用:
上述代码已经展现了迭代器begin函数和end函数的复刻,而rbegin和rend只不过是反向迭代器换个方向而已
范围for(其实本质就是调用了个迭代器)
//基础格式
for(auto:arr)
{
arr[i] += 1; //arr数组中每个数都加1
}
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加字符串 |
operator+= | 在字符串后追加字符串str |
c_str | 返回c格式字符串 |
find + npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfifind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
这些函数的复刻大多都在上面实现了,基本用法也是比较容易,这里给大家演示一下find的用法。
int main()
{
string file("test.txt");
FILE* fout = fopen(file.c_str(),"w");
size_t pos = file.find('.');
if(pos != string::npos)
{
string suffix = file.substr(pos,file.size() - pos);
cout<<suffix<<endl; //打印为“.text"
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。