如果调试一个程序让你很苦恼,千万不要放弃,成功永远在拐角之后,除非你走到拐角,否则你永远不知道你离他多远,所以,请记住,坚持不懈,直到成功。
目录
前言
1.string类的常用接口
1.1string类对象的常见构造:
1.2string类对象的访问及遍历操作
1.3string类对象的容量操作
1.4string类对象的修改操作:
2.string类的模拟实现
3.string类的习题
前言
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。学会使用STL,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。
本篇文章介绍string类,学习string类我们大致分两个方面进行,首先是学习如何使用库中string接口,然后再通过学习string底层源代码的进行模拟实现接口
在介绍string接口前,首先介绍一个网站可以用来查询C++中的库函数以及STL,后续的学习当中会经常在这个文档上查阅各种接口——https://legacy.cplusplus.com/
1.string类的常用接口
string类包含许多接口,在这里我们主要重点介绍string类的常用接口,对于常用的接口我们需要做到不用查阅文档就可以熟练的使用,对于其它接口,只需要做到知道有这种接口,查阅文档会使用即可
1.1string类对象的常见构造:
函数名称 功能说明 string() 构造空的string类对象,即空字符串 string(const char* s) 用C-string来构造string类对象 string(const string&s) 拷贝构造函数 string(size_t n, char c) string类对象中包含n个字符c
1.2string类对象的访问及遍历操作
函数名称 功能说明 operator[] 返回pos位置的字符,const string类对象调用 范围for C++11支持的更简洁的范围for遍历方式 begin + end begin获取第一个字符的迭代器 , end获取最后一个字符下一个位置的迭
代器rebing + rend rbegin获取最后一个字符下一个位置的迭代器,rend获取第一个字符的迭代器
operator[]:
范围for:
上面是两种string类对象常规的遍历方式,下面介绍一种STL中通用的遍历方式就是迭代器
迭代器在行为上像指针一样的类型
正向遍历:begin + end
反向遍历:rebegin + rend
以上是关于迭代器正向遍历和反向遍历的两种方式,除此之外,库中还有const修饰的函数
如:
为什么会存在const修饰的函数呢?通过下面的代码来说明:
这段代码中我们只是想打印这个字符串,并不做任何的修改,因此可以使用引用,这样做就减少了一次拷贝构造,提高程序的效率,所以在库中也定义了const修饰的函数
下面来介绍一个在编写代码时的小技巧:
上面两行代码是等价的,在使用熟练之后,如果在编写代码的过程中如果觉得定义rit这个变量时类型过长,可以使用auto这个关键字,因为auto可以自动推导变量类型
1.3string类对象的容量操作
size和length函数是等价的:
函数名称 功能说明 size 返回字符串有效长度 length 返回字符串有效长度 capacity 返回空间总大小 clear 清空有效字符 reserve 为字符串预留空间 resize 将有效字符的个数该成n个,多出的空间用字符c填充 capacity函数:
clear函数:
reserve函数:
关于reserve这个函数的好处是什么呢?
string这个类在构造对象的时候,底层是使用动态增长的方式来按需申请和释放内存的,如果在构造之前就知道大概需要多大的空间,就不用频繁的申请内存,有效避免在堆上频繁申请内存,造成内存碎片的问题
resize函数:
功能:将有效字符改成n个,多出的空间用字符c来填充
因此会存在三种情况:
n < size :删除数据
size < n <= capacity :插入数据
n > capacity :扩容 + 插入数据
第一种情况:
第二种情况:
第三种情况:
1.4string类对象的修改操作:
函数名称 功能说明 push_back 在字符串后面尾插字符c append 在字符串后面追加一个字符串 operator+= 在字符串后面追加字符串 c_str 返回c格式字符串 find 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 substr 在str中从pos位置开始,截取n个字符,然后将其返回
insert 在pos位置插入字符 erase 删除pos位置处的n个字符 assign 将一个字符串拷贝到一个对象中去 replace 从pos位置开始,将n个字符改成n个字符 push_back函数:
append函数:
operator+=:
c_str函数:返回一个指向该字符串首字符的指针
find函数:
rfind函数:
注:find函数和rfind如果找不到就返回npos
-1是size_t类型转换后就是最大的整型:
substr函数:从pos位置开始,截取n个字符,返回截取字符第一个字符的地址
insert函数:
erase函数:
assign函数:
repalce函数:
2.string类的模拟实现
1.定义一个string类:为了和库里的区分将类名定义为String
class String { public: //成员函数 private: //成员变量 };
接口1:
构造函数:
//无参构造: String() { char* _str = new char[1]; _str[0] = '\0'; _capacity = _size = 0; } //带参构造: String(const char* str = "") { _size = strlen(str); _capacity = _size; _str = new char[_capacity + 1]; strcpy(_str, str); }
接口2:
拷贝构造函数:
//传统写法: String(const String& s) { _str = new char[s._capacity + 1]; _capacity = s._capacity; _size = s._size; strcpy(_str, s._str); } //现代写法 String(const String& s) :_str(nullptr) ,_size(0) ,_capacity(0) { String tmp(s._str); std::swap(_str, tmp._str); std::swap(_size, tmp._size); std::swap(_capacity, tmp._capacity); } //现代写法的优化: void swap(String& s) { std::swap(_str, s._str);//内置类型调用算法库里面的 std::swap(_size, s._size); std::swap(_capacity, s._capacity); } String(const String& s) :_str(nullptr) , _size(0) , _capacity(0) { String tmp(s._str); //this->swap(tmp); swap(tmp);//自定义类型使用自己的swap,避免了使用算法库里面的产生三次深拷贝 }
接口3
析构函数:
~String() { delete[] _str; _str = nullptr; _capacity = _size = 0; }
接口4
赋值重载函数:
//传统写法 String& operator=(const String& s) { if (this != &s) { char* tmp = new char[s._capacity + 1]; strcpy(tmp, s._str); delete[] _str; _str = tmp; _size = s._size; _capacity = s._capacity; } return *this; } //现代写法: String& operator=(const String& s) { if (this != &s) { String tmp(s); swap(tmp); } return *this; } //现代写法的优化 String& operator=(String s) { if (this != &s) { swap(s); } return *this; }
接口5
operator[]:
char& operator[](size_t pos) { return _str[pos]; }
接口6
size():
size_t size() { return _size; }
接口7
capacity():
size_t capacity() { return _capacity; }
接口8:
c_str():
char* c_str() { return _str; }
接口9:
begin和end
typedef char* iterator; iterator begin() { return _str; } iterator end() { return _str + _size; }
接口10
reserve():
void reserve(int n) { if (n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } }
接口11
push_back():
void push_back(char ch) { if (_size == _capacity) { int newCapacity = _capacity == 0 ? 4 : _capacity * 2; reserve(newCapacity); } _str[_size] = ch; ++_size; _str[_size] = '\0'; }
接口12
append():
void append(const char* str) { size_t len = strlen(str); if (_size+len > _capacity) { reserve(_size + len); } strcat(_str, str); _size += len; }
接口13
operator+=():
//+=字符 String& operator+=(const char ch) { push_back(ch); return *this; } //+=字符串 String& operator+=(const char* str) { append(str); return *this; }
接口14
insert():
//插入字符 String& insert(size_t pos, const char ch) { assert(pos < _size); if (_size == _capacity) { int newCapacity = _capacity == 0 ? 4 : _capacity * 2; reserve(newCapacity); } //写法1: /* int end = _size; while (end >= (int)pos) { _str[end + 1] = _str[end]; --end; }*/ //写法2: int 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* str) { size_t len = strlen(str); if (_size + len > _capacity) { reserve(_size + len); } //写法1: /* int end = _size; while (end >= (int)pos) { _str[end + len] = _str[end]; --end; }*/ //写法2: int end = _size + len; while (end >= pos + len) { _str[end] = _str[end - len]; --end; } strncpy(_str + pos, str, len); _size += len; return *this; }
接口15
erase():
String& erase(size_t pos, size_t len = npos) { assert(pos < _size); if (len == npos || len >= _size - pos) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } return *this; }
接口16
find():
//查找字符 size_t find(char ch, size_t pos = 0) { assert(pos < _size); int i = pos; while (_size--) { if (_str[i] == ch) { return i; } i++; } return npos; } //查找字符串: size_t find(const char* str, size_t pos = 0) { assert(pos < _size); char* ptr = strstr(_str + pos, str); if (ptr == nullptr) { return npos; } else { return ptr - _str; } }
接口17
clear():
void clear() { _str[0] = '\0'; _size = 0; }
接口18
resize():
void resize(size_t n, char ch = '\0') { if (n > _capacity) { reserve(n); for (int i = _size; i < n; i++) { _str[i] = ch; } _size = n; _str[_size] = '\0'; } else { _str[_size] = '\0'; _size = n; } }
流提取:oparetor<<()
ostream& operator<<(ostream& out, String& s) { for (int i = 0; i < s.size(); i++) { out << s[i]; } return out; }
流插入:oparetor>>()
istream& operator>>(istream& in, String& s) { s.clear(); //第一种写法 //char ch = in.get(); 等于空格或者换行就就结束了! //while (ch != ' ' && ch != '\n') //{ // s += ch; // ch = in.get(); //} // 第一种写法的优化 //减少频繁扩容 char buf[128] = { 0 }; int i = 0; char ch = in.get(); while (ch != ' ' && ch != '\n') { if (i == 127) { s += buf; i = 0; } buf[i++] = ch; ch = in.get(); } //不足128或者剩下的: if (i > 0) { buf[i] = '\0'; s += buf; } return in; }
3.string类的习题
习题1:字符串转换成整数oj链接
class Solution { public: int StrToInt(string str) { int sum = 0; int flag = 1; for(int i = 0; i<str.size(); i++) { if(str[i] == '+' || str[i] == '-') { flag = str[i] == '+'? flag : -flag; continue; } else if(!(str[i] >= '0' && str[i] <= '9')) { return 0; } else { sum = sum*10 + str[i] - '0'; } } return sum*flag; } };
习题2:字符串相加oj链接
class Solution { public: string addStrings(string num1, string num2) { int end1 = num1.size()-1; int end2 = num2.size()-1; int carry = 0; string retStr; retStr.reserve(max(num1.size(),num2.size()+1)); while(end1 >= 0 || end2 >= 0) { int val1 = end1 >= 0 ? num1[end1] - '0' : 0; int val2 = end2 >= 0 ? num2[end2] - '0' : 0; int ret = val1 + val2 + carry; carry = ret / 10; ret %= 10; retStr += '0' + ret; end1--; end2--; } if(carry == 1) retStr += '1'; reverse(retStr.begin(),retStr.end()); return retStr; } };
习题4:仅仅反转字母oj链接
class Solution { public: string reverseOnlyLetters(string s) { int begin = 0; int end = s.size()- 1; while(begin < end) { while(begin < end && !isalpha(s[begin])) { ++begin; } while(begin < end && !isalpha(s[end])) { --end; } swap(s[begin],s[end]); ++begin; --end; } return s; } };
习题5:字符串中只出现一次的字符oj链接
class Solution { public: int firstUniqChar(string s) { int str[26] = { 0 }; for(auto& ch : s) { str[ch - 'a']++; } for(int i = 0; i < s.size(); i++) { if(str[s[i]-'a'] == 1) return i; } return -1; } };
习题6:字符串中最后一个单词的长度oj链接
#include<iostream> #include<string> using namespace std; int main() { string s; getline(cin,s); size_t pos = s.rfind(' '); cout<<s.size()-pos-1 <<endl; return 0; }
习题7:验证字符串是否回文oj链接
class Solution { public: bool isCharacter(char ch) { return ((ch >= '0' && ch <= '9') ||(ch >= 'a' && ch <= 'z') ||(ch >= 'A' && ch <= 'Z')); } bool isPalindrome(string s) { for(auto& ch : s) { if(ch >= 'A' && ch <= 'Z') { ch +=32; } } int begin = 0; int end = s.size()-1; while(begin < end) { while(begin < end && !isCharacter(s[begin])) { ++begin; } while(begin < end && !isCharacter(s[end])) { --end; } if(s[begin] != s[end]) return false; else { ++begin; --end; } } return true; } };
习题8:反转字符串(区间部分反转)oj链接
class Solution { public: string reverseStr(string s, int k) { int n = s.size(); for(int i = 0; i<n; i+=2*k) { reverse(s.begin()+i,s.begin() + min(i+k,n)); } return s; } };
习题9:反转字符串中的单词oj链接
class Solution { public: string reverseWords(string s) { int len = s.size(); int begin = 0; int end = 0; while(begin < len) { while(s[end] != ' ' && s[end] != '\0') { ++end; } int end1 = end+1; while(begin < end) { swap(s[begin],s[end-1]); ++begin; --end; } begin = end1; end = end1; } return s; } };