深入刨析 之C++ string类
a. string是表示字符串的字符串类。 b. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。 c. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string。 d. 不能操作多字节或者变长字符的序列。 注意:在使用string类时,必须包含#include头文件以及using namespace std; resize(size_t) resize(size_t n,char c) 注意: 1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。 2. clear()只是将string中有效字符清空,不改变底层空间大小。 3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。 4. reserve(size_t n=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。 注意: 1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。 2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。 at()和operator[]都是根据下标获取任意位置元素的,在debug模式下两者都会去做边界检查。 当发生越界行为时,at是抛异常,operator[]内部是assert会触发。 注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间: a. 当字符串长度小于16时,使用内部固定的字符数组来存放。 b. 当字符串长度大于等于16时,从堆上开辟空间。 这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。 其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量 最后:还有一个指针做一些其他事情。 故总共占16+4+4+4=28个字节。 G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段: a. 空间总大小 b. 字符串有效长度 c. 引用计数 需注意: 浅拷贝和是否拷贝完全 C的字符串和string的区别: ①C的字符数组,是以'\0'为终止算长度。 ②string不看'\0',以size(有效字符)为终止长度。 传统写法和现代写法 虽然传统写法和现代写法的效率都是差不多的,但是现代写法,代码比较简洁明了。 注意当在0位置是插入的时候需谨慎处理,涉及到隐式类型的转换。欢迎来到干货小仓库!!!
没有完美的计划,每个人都在试验的过程中渐渐清晰!!!
1.标准库的string类
2.string类的常用接口说明
2.1string类对象的常见构造
函数名称 功能说明 string() 构造空的string类对象,即空字符 string(const char* s) 用C语言形式的字符串来构造string类对象 string(size_t n,char c) string类对象中包含n个字符c string(const string& s) 拷贝构造函数 #include<string>using namespace std;int main(){ string s1; // 构造空的string类对象s1string s2("hello bit"); // 用C格式字符串构造string类对象s2string s3(s2); // 拷贝构造s3return 0;}
2.2string类对象的容量操作
函数名称 功能说明 size() 返回字符串有效长度 length() 返回字符串有效长度 capacity() 返回空间大小 empty() 检测字符串是否为空串,是返回false,不是返回true clear() 清空有效字符 reserve(size_t n=0) 为字符串预留空间 将有效字符的个数变成n个,多出的空间用字符c填充,若没指定字符默认用'\0'填充。 2.3string类对象的访问及遍历操作
函数名称 功能说明 push_back 在字符串后插入字符c append 在字符串后追加一个字符串 operator+= 在字符串后追加字符串str c_str 返回C格式字符串 find+npos rfind substr 2.3.1at()和operator[]的区别
2.4string类非成员函数
函数 功能说明 operator+ 尽量少用,因为是传值返回,导致深拷贝效率低 operator>> 输入运算符重载 operator<< 输出运算符重载 getline 获取一行字符串 relational operators 大小比较 2.5vs和g++下的string结构的说明
2.5.1vs下string的结构
union _Bxty{ // storage for small buffer or pointer to larger one value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // to permit aliasing} _Bx;
2.5.2g++下的string的结构
struct _Rep_base{ size_type _M_length; size_type _M_capacity; _Atomic_word _M_refcount;};
3.string类的模拟实现需注意的问题
3.1构造函数和拷贝构造
//构造函数string(const char* str = ""){ _size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//strcpy(_str, str);拷贝到'\0'就停止了memcpy(_str, str, _size + 1);//逐字节拷贝}//拷贝构造string(const string& s){ _str=new char[s.capacity+1];//strcpy(_str,s.str) 这种情况"hello\0world"会拷贝不完全memcpy(_str,s._str,s._size+1);//逐字节拷贝_size=s._size;_capacity=s._capacity;}
3.2赋值拷贝
//传统写法string& operator=(const string& s){ if(this != &s){ char* tmp=new char[s._capacity+1]; memcpy(tmp,s._str,s._size+1); delete[] _str; _str=tmp; _size=s._size; _capacity=s._capacity;} return *this;}void swap(string& s ){ //复用算法库中的交换 std::swap(_str,s._str); std::swap(_size,s._size); std::swap(_capacity,s._capacity);}//现代写法string& operator=(string tmp){ swap(tmp); return *this;}
3.3insert接口的实现
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; while (end >= pos && end != npos) { _str[end + len] = _str[end]; --end; } //填数据 for (int i = 0; i < len; i++) { _str[pos++] = s[i]; } _size += len; return *this;}
3.4迭代器
typedef char* iterator;typedef const char* const_iterator;iterator begin(){ return _str;}iterator end(){ return (_str + _size);}const_iterator begin() const{ return _str;}const_iterator end() const{ return (_str + _size);}
4.string常用接口的模拟实现代码
#include<assert.h>#include<iostream>#include<string.h>#include<string>using namespace std; typedef char* iterator; typedef const char* const_iterator; class string { public: //构造函数 string(const char* str = "") { _size = strlen(str); _capacity = _size; _str = new char[_capacity + 1]; //strcpy(_str, str); memcpy(_str, str, _size + 1); } //写法一 //string(const string& str) //{ // _size = str._size; // _capacity = str._capacity; // _str = new char[_capacity + 1]; // //strcpy(_str, str._str); // memcpy(_str, str._str, _size + 1); //} //写法二 string(const string& s) :_str(nullptr) ,_size(0) ,_capacity(0) { string tmp(s._str); swap(tmp); } char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; } iterator begin() { return _str; } iterator end() { return (_str + _size); } const_iterator begin() const { return _str; } const_iterator end() const { return (_str + _size); } void reserve(size_t n = 0) { if (n > _capacity) { //cout << "reserve()" << n << endl; char* tmp = new char[n + 1]; //strcpy(tmp, _str); memcpy(tmp, _str, _size + 1); _capacity = n; delete[] _str; _str = tmp; } } void push_back(char c) { if (_size == _capacity) { //2倍扩容 reserve(_capacity == 0 ? 4 : _capacity * 2); } _str[_size] = c; _size++; _str[_size] = '\0'; } string& append(const char* s) { size_t len = strlen(s); if (_size + len > _capacity) { //至少扩容到_size + len reserve(len + _size); } //strcpy(_str + _size, s); memcpy(_str + _size, s, len + 1); _size += len; return *this; } string& operator+=(char ch) { push_back(ch); return *this; } string& operator+=(const char* s) { append(s); return *this; } /*size_t capacity() { return _capacity; }*/ size_t size() const { return _size; } string& insert(size_t pos, size_t n, char ch) { assert(pos <= _size); if (_size + n > _capacity) { //至少扩容到_size+n reserve(_size + n); } //移数据 //写法一 //注意有整型提升 /*int end = _size; while (end >= (int)pos) { _str[end+n] = _str[end]; --end; }*/ //写法二 size_t end = _size; while (end >= pos && end != npos) { _str[end + n] = _str[end]; --end; } //填数据 for (int i = 0; i < n; i++) { _str[pos++] = ch; } _size += n; 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; while (end >= pos && end != npos) { _str[end + len] = _str[end]; --end; } //填数据 for (int i = 0; i < len; i++) { _str[pos++] = s[i]; } _size += len; return *this; } string& erase(size_t pos = 0, size_t len = npos) { assert(pos <= _size); if (len == npos || pos + len > _size) { _size = pos; _str[pos] = '\0'; } else { size_t end = pos + len; while (end <= _size) { _str[pos++] = _str[end++]; } _size -= len; } return *this; } size_t find(const char* s, size_t pos = 0) { assert(pos <= _size); char* ptr = strstr(_str, s); if (ptr == nullptr) { return npos; } else { return ptr - _str; } } string substr(size_t pos, size_t len = npos) { assert(pos < _size); size_t n = len; if (len == npos || pos + len > _size) { n = _size - pos; } string tmp; tmp.reserve(n); for (size_t i = pos; i < pos + n; i++) { tmp += _str[i]; } return tmp; } void resize(size_t n,char ch='\0') { if (n < _size) { _size = n; _str[_size] = '\0'; } else { reserve(n); for (size_t i = _size; i < n; i++) { _str[i] = ch; } _size = n; _str[n] = '\0'; } } /* bool operator<(const string& s) { size_t size = (_size < s._size ? _size : s._size); for (size_t i = 0; i < size; i++) { if (_str[i] < s._str[i]) return true; else if (_str[i] > s._str[i]) return false; } if (_size < s._size) return true; else return false; }*/ bool operator<(const string& s) { int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size); //"hello" "hello" false //"helloxxx" "hello" false //"hello" "helloxxx" true return ret == 0 ? _size < s._size : ret < 0; } bool operator==(const string& s) { return (_size == s._size) && memcmp(_str, s._str, _size < s._size ? _size : s._size) == 0; } bool operator!=(const string& s) { return !(*this == s); } bool operator<=(const string& s) { return (*this < s) || (*this == s); } bool operator>(const string& s) { return !(*this <= s); } bool operator>=(const string& s) { return !(*this < s); } void swap( string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } //传统写法一 /*string& operator=(const string& s) { if (this != &s) { char* tmp = new char[s._capacity + 1]; memcpy(tmp, s._str, s._size); 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 tmp) { swap(tmp); return *this; } void clear() { _str[0] = '\0'; _size = 0; } ~string() { delete[] _str; _str = nullptr; _size = _capacity = 0; } char* c_str() const { return _str; } private: size_t _size; //有效数据不包含'\0' size_t _capacity; char* _str; public: static size_t npos;// }; size_t string::npos = -1; ostream& operator<<(ostream& out, const string& s) { /*for (int i = 0; i < s.size(); i++) { out << s[i]; }*/ for (auto e : s) { out << e; } return out; } istream& operator>>(istream& _cin, string& s) { s.clear(); char ch = _cin.get(); while (ch == ' ' || ch == '\n') { ch = _cin.get(); } int i = 0; char str[128]; /*while (ch != ' ' && ch != '\n') { s += ch; ch = _cin.get(); }*/ while (ch != ' ' && ch != '\n') { str[i++] = ch; if (i == 127) { str[i] = '\0'; s += str; i = 0; } ch = _cin.get(); } if (i != 0) { str[i] = '\0'; s += str; } return _cin; }
觉得不错的记得点赞+收藏咯!!!
- 最近发表
- 随机阅读
-
- W800BT漫步者 Freee主动降噪蓝牙耳机云白推广
- ThinkPad E14 2023年轻薄本13代i5
- Conda/Miniconda/Anaconda 安装及命令整理
- Spring中基于redis stream 的消息队列实现方法
- Spacex载人龙飞船Crew
- 跑酷游戏下载列表:推荐五款免费游戏
- 【MySQL】操作MySQL库
- 《歌手》水月雨 2024年联名版无线降噪头戴耳机官方公告 7 月 19 日首发
- 温室气体排放可能对卫星构成威胁
- 在线Hash文本工具
- 如何快速免费.转换mp4(不限).amv&&在MP4播放设备上观看视频
- 2024云计算省赛总结(2),读完我这份《大数据开发开发核心源码精编解析》面试至少多要3K
- 使用Arduino ESP322IDE
- 马自达MX2024
- 年中总结,塌实木棉树的市场竞争力
- 零刻SER9迷你主机性能强的办公娱乐神器
- 永艺沃克Pro人体工程学椅黑色带搁脚舒适座椅
- 探索神秘世界:黑神话启动器揭开了宏大游戏的序幕
- 【完美解决】Error : java 错误 : 发行版本5不支持
- Java的自动装箱和自动拆箱
- 搜索
-
- 友情链接
-