目录

string构造函数重载的形式比较多样,最常用的是1. 默认构造函数 2. 拷贝构造函数 4. 以C字符串-const char* 为参数的构造函数。
其他的用的并不多。比如,string从pos位置开始的len个,C字符串的前n个,n个char,还有迭代器范围版本。
- void test1()
- {
- string s0("12345abc"); // string(const char* s);
- string s1; // string();
- string s2(s0); // 拷贝构造,string(const string& s);
- string s3 = s0; // 拷贝构造,C方式的拷贝构造。
- string s4 = "aaaaa";
- }
s2 为典型的拷贝构造,s3为C语言风格的拷贝构造。 值得注意的是s4,这里因为string(const char*) 版本构造函数不是explicit的,所以"aaaaa"隐式类型转换为(构造)string对象,再拷贝构造s4,编译器对此情况优化为直接进行构造。
当explicit string(const char* s); 时

解除explicit后,如果在string(const char* s)构造函数和拷贝构造中加上输出语句,运行这个构造代码后,发现并没有拷贝构造的输出语句,即编译器对此优化为了直接构造。

分析:比较简单,operator=重载了多个版本,注意区分赋值和构造的情况。析构函数在对象销毁时自动调用,用于回收清理对象申请的空间。
数据成员只有这几个。
- private:
- char* _str;
- size_t _size;
- size_t _capacity;
- public:
- const static size_t npos = -1;
(都是class string的成员函数)
- // string() : _size(0),_capacity(0),_str(new char[1])
- // {
- // _str[0] = '\0';
- // }
- string(const char* str = "")
- {
- _size = strlen(str); // strlen不包含\0,计算的是有效字符个数。
- _capacity = _size;
- _str = new char[_capacity+1];
-
- strcpy(_str, str); // strcpy会拷贝'\0'
- }
- string(size_t n, char c)
- : _size(n),_capacity(n),_str(new char[n+1])
- {
- for(size_t i = 0; i < n; ++i)
- _str[i] = c;
- _str[n] = '\0';
- }
没有全部实现,实现了几个常用的,明确size和capacity的意义,利用C语言库函数和new即可简单实现。
- string(const string& s)
- : _size(s._size),_capacity(s._capacity),_str(new char[s._capacity+1])
- {
- strcpy(_str,s._str);
- }
- // s1 = s3;
- string& operator=(const string& s)
- {
- if(this != &s)
- {
- delete[] this->_str;
- _str = new char[s._capacity+1];
- _size = s._size;
- _capacity = s._capacity;
- strcpy(_str,s._str);
- }
- return *this;
- }
- // 略微改进了一点,考虑到如果new开空间失败,不破坏原对象。
- string& operator=(const string& s)
- {
- if(this != &s)
- {
- char* tmp = new char[s._capacity+1];
- strcpy(tmp,s._str);
- delete[] this->_str;
- _str = tmp;
- _size = s._size;
- _capacity = s._capacity;
- }
- return *this;
- }
- void swap(string& tmp)
- {
- std::swap(_str,tmp._str);
- std::swap(_size,tmp._size);
- std::swap(_capacity,tmp._capacity);
- }
- // 现代写法: 老板思维
- string(const string& s) // string s1(s2)
- :_size(0),_capacity(0),_str(nullptr) // 如果不这样,this的_str是随机值,tmp析构时,delete[]随机值非法。
- {
- string tmp(s._str); // 调用string(const char*) 构造函数
- this->swap(tmp);
- }
- string& operator=(const string& s)
- {
- if (this != &s) {
- // string tmp(s);
- string tmp(s._str);
- swap(tmp);
- }
- return *this;
- }
- // 最简版
- string& operator=(string s)
- {
- swap(s);
- return *this;
- }
-
- string& operator=(const char* s) {
- string tmp(s);
- this->swap(tmp);
- return *this;
- }
- ~string()
- {
- delete[] _str;
- _str = nullptr;
- _size = _capacity = 0;
- }
1. 拷贝构造的现代写法:利用string(const char* s)构造出临时对象tmp,其实就是用实参string拷贝一份,然后交换*this 和 tmp,因为这里的*this刚刚初始化完,数据成员都是随机值,如果和tmpswap之后,tmp析构时,数据成员是随机值会报错,所以利用初始化列表将*this数据成员简单初始化以下,再与tmp交换。
2. 对于swap函数,这里利用的是string的成员函数swap,上方有实现。原因有2:a、标准库中的std::swap()对于交换的两个对象,是先拷贝构造一份,再赋值两次,对于深拷贝的自定义类型来说,效率较低。 b、std::swap()会调用交换对象的类型的拷贝构造和operator=,我们在实现的拷贝构造中使用std::swap就会造成无限递归。 所以必须使用string的成员swap,而string::swap又调用std::swap()去交换内置类型数据成员。
3. 最简版是对上方的进一步简化,在string这里,各个版本差别不是很大,但是到了vector,list等较复杂容器时,现代写法的优势将会很明显。
STL中的容器,统一的访问元素方式为迭代器访问,string同样支持,但是由于string底层存储是连续的,所以operator[] at() 会更加方便一些。

重载了const 和 非const两个版本,配对string 和 const string,对于非const string而言,[]运算符是返回元素引用的,可读可写。类似于C语言的数组,使得访问元素更加方便。

at于operator[]的功能一样,只是at函数在pos越界时,抛出异常,而[]运算符下标越界时直接崩溃。
back() front() 分别返回首元素和尾元素的引用,同样重载了const和非const两个版本。略了。
- void test2()
- {
- string s("1234567");
- for(size_t i = 0; i < s.size(); ++i)
- {
- ++s[i];
- ++s.at(i);
- cout<
- cout<
at(i)<<" "; - }
- cout<
- }
实现:
- char& operator[](size_t pos)
- {
- assert(pos < _size);
- return _str[pos];
- }
- const char& operator[](size_t pos) const
- {
- assert(pos<_size);
- return _str[pos];
- }
string - 迭代器iterator
简介:
迭代器,用于访问容器的元素,使用上类似于指针。事实上,对于string,vector这种底层存储为连续存储的容器而言,它们的迭代器就是一个改了名字的元素指针而已。而对于list map等存储不连续且复杂的容器而言,不再是普通的元素指针。
对于string而言,使用[]访问元素会比迭代器更加方便,但是迭代器对于STL的其他容器而言至关重要,有些容器只能使用迭代器访问元素。迭代器的优点在于:通用性,简便性,对于STL所有容器来说,用法几乎一致。
介绍:
1. STL容器包括string,一共有四种迭代器,
iterator const_iterator reverse_iterator const_reverse_iterator
分别为迭代器,const迭代器,反向迭代器,const反向迭代器。
3. begin() end() rbegin() rend() cbegin() cend() crbegin() crend() 分别为返回迭代器的成员函数。
4. begin end 分别对应正向迭代器,rbegin rend对应反向迭代器


上述四个成员函数,都重载了两个版本,用于const容器对象和非const容器对象,使用时自动匹配。那么,由此可见,这四个成员函数就足够得到全部种类的迭代器了,那后面四个又有什么用呢? C++11新增了cbegin cend crbegin crend,用以精确返回迭代器类型。提高代码可读性。也就可以让非const容器对象指定返回const_iterator。
使用:
1. 迭代器++,自动指向下一个元素,对于string,vector这种容器,迭代器++其实就是元素指针++,而对于list等底层存储不连续的容器,++为指向下一个元素,但是并不是内存中指针的++。
- void test3() {
- string s("abcdef");
- string::iterator it1 = s.begin();
- while (it1 != s.end()) {
- (*it1)++;
- cout << *it1;
- ++it1;
- }
- cout << endl;
- string::const_reverse_iterator it2 = s.crbegin();
- while (it2 != s.crend()) {
- cout << *it2;
- ++it2;
- }
- cout << endl;
-
- const string s1("123456");
- for (string::const_iterator it = s1.begin(); it != s1.end(); ++it) {
- // it为const_iterator
- // (*it)++; // error
- cout << *it;
- }
-
- for (auto i: s1) {
- cout << i;
- }
- for (const auto &i: s1) {
- cout << i;
- }
- }
2. 范围for的原理就是迭代器,编译器会将范围for语句自动编译为一系列关于迭代器的语句。第一个为 char i = *it 第二个为 const char& i = *it
7. 解引用迭代器得到元素的引用。 begin end 左闭右开。 使用迭代器时,使用it != s.end() 而不是it < s.end() ,对于string vector,迭代器支持<运算符,但是对于list等,由于底层存储不连续,迭代器不支持<运算符。
- public:
- 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;
- }
- const_iterator cbegin() const
- {
- return _str;
- }
- const_iterator cend() const
- {
- return _str+_size;
- }
简单实现,反向迭代器需要其他的学习。
string - 大小,容量函数
1. size_t size() const; 返回string中有效字符个数(string底层存储时末尾有一个'\0',与C语言一样,此'\0'不算有效字符)
2. size_t capacity() const; 返回string可容纳字符个数,string中有些空间已经申请好了,但是没有存储字符,不算是有效字符,但是属于string的空间。
3. void resize (size_t n); void resize (size_t n, char c); resize用于改变string的size大小,可减小,可扩大,扩大后默认用'\0'填充。size减小后,会删除元素,但是不会改变capacity
4. void reserve (size_t n = 0); 用于改变capacity,当n<=capacity时,无操作。当n>capacity时,会扩充容量,即申请额外的空间,但是不存储有效字符。
5. void clear(); 用于清理string,清理后size为0 capacity不变
6. bool empty() const; 用于判空
7. void shrink_to_fit(); 释放多余的capacity,缩减到size大小。 有时不会进行操作。
string - 修改string操作
0. operator+=
1. void push_back (char c); 末尾追加一个字符
1.5. void pop_back(); ..... 略
2. append 在string末尾追加,重载多个版本,和构造函数的参数形式几乎一样。
3. assign 给string重新赋值,作用等同于 operator=,和append,构造函数重载形式十分类似。
4. insert 给某一下标位置插入某些字符,重载形式还是那一套,只是多了个pos参数,有点无趣。
5. erase 重载三个版本,需要查询网站即可。
6. replace 重载好多版本,其实观察一下还是那一套,pos位置len个修改为各种形式,需要时查阅
7. void swap (string& str); 这个是string的成员函数,将调用对象和参数str交换。
总结:append push_back 直接用+=即可,assign用operator=即可,insert erase replace 用处较小,用时查阅。
string - 其余操作
1. const char* c_str() const; 返回C形式的字符串首地址。
2. string substr (size_t pos = 0, size_t len = npos) const; 取当前字符串的字串,从pos开始len个字符,返回一个新字符串。
3. 各种find,用于查找子串。 莫名其妙的copy,不想弄了,心态崩了。
完整tiny string
- #ifndef STL_STRING_H
- #define STL_STRING_H
-
- namespace yzl
- {
- class string
- {
- public:
- 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;
- }
- const_iterator cbegin() const
- {
- return _str;
- }
- const_iterator cend() const
- {
- return _str+_size;
- }
- public:
- // string() : _size(0),_capacity(0),_str(new char[1])
- // {
- // _str[0] = '\0';
- // }
- string(const char* str = "")
- {
- _size = strlen(str); // strlen不包含\0,计算的是有效字符个数。
- _capacity = _size;
- _str = new char[_capacity+1];
-
- strcpy(_str, str); // strcpy会拷贝'\0'
- }
- string(size_t n, char c)
- : _size(n),_capacity(n),_str(new char[n+1])
- {
- for(size_t i = 0; i < n; ++i)
- _str[i] = c;
- _str[n] = '\0';
- }
- // 传统写法
- // string(const string& s)
- // : _size(s._size),_capacity(s._capacity),_str(new char[s._capacity+1])
- // {
- // strcpy(_str,s._str);
- // }
- // s1 = s3;
- // string& operator=(const string& s)
- // {
- // if(this != &s)
- // {
- // delete[] this->_str;
- // _str = new char[s._capacity+1];
- // _size = s._size;
- // _capacity = s._capacity;
- // strcpy(_str,s._str);
- // }
- // return *this;
- // }
- // 略微改进了一点,考虑到如果new开空间失败,不破坏原对象。
- // string& operator=(const string& s)
- // {
- // if(this != &s)
- // {
- // char* tmp = new char[s._capacity+1];
- // strcpy(tmp,s._str);
- // delete[] this->_str;
- // _str = tmp;
- // _size = s._size;
- // _capacity = s._capacity;
- // }
- // return *this;
- // }
-
- //=======================================================
- void swap(string& tmp)
- {
- std::swap(_str,tmp._str);
- std::swap(_size,tmp._size);
- std::swap(_capacity,tmp._capacity);
- }
- // 现代写法: 老板思维
- string(const string& s) // string s1(s2)
- :_size(0),_capacity(0),_str(nullptr) // 如果不这样,this的_str是随机值,tmp析构时,delete[]随机值非法。
- {
- string tmp(s._str); // 调用string(const char*) 构造函数
- this->swap(tmp);
- }
- // string& operator=(const string& s)
- // {
- // if (this != &s) {
- string tmp(s);
- // string tmp(s._str);
- // swap(tmp);
- // }
- // return *this;
- // }
- // 最简版
- string& operator=(string s)
- {
- swap(s);
- return *this;
- }
-
- string& operator=(const char* s) {
- string tmp(s);
- this->swap(tmp);
- return *this;
- }
-
- ~string()
- {
- delete[] _str;
- _str = nullptr;
- _size = _capacity = 0;
- }
-
- const char* c_str() const
- {
- return _str;
- }
- size_t size() const
- {
- return _size;
- }
- size_t capacity() const
- {
- return _capacity;
- }
- char& operator[](size_t pos)
- {
- assert(pos < _size);
- return _str[pos];
- }
- const char& operator[](size_t pos) const
- {
- assert(pos<_size);
- return _str[pos];
- }
-
- void reserve(size_t n = 0)
- {
- if(n > _capacity)
- {
- char* tmp = new char[n+1]; // n个有效数据,1用于'\0'
- strcpy(tmp,_str);
- delete[] (_str);
- _str = tmp;
- _capacity = n; // _capacity 必须处理
- }
- }
- void resize(size_t n, char c = '\0')
- {
- if(n > _size)
- {
- reserve(n);
- for(size_t i = _size; i < n; ++i)
- {
- _str[i] = c;
- }
- _str[n] = '\0';
- _size = n;
- }
- else
- {
- _str[n] = '\0';
- _size = n;
- }
- }
- void clear()
- {
- _str[0] = '\0';
- _size = 0;
- }
- bool empty() const
- {
- return _size == 0;
- }
- void push_back(char c)
- {
- if(_size == _capacity)
- {
- reserve(_capacity == 0 ? 4 : 2*_capacity);
- }
- _str[_size] = c;
- ++_size;
- _str[_size] = '\0';
- }
- string& append(const char* s)
- {
- int size = strlen(s);
- if(_size + size > _capacity)
- {
- reserve(_size+size);
- }
- strcpy(_str+_size,s);
- _size+=size;
- return *this;
- }
- string& append(const string& s)
- {
- int size = s.size();
- if(_size + size > _capacity)
- {
- reserve(_size + size);
- }
- strcpy(_str+_size,s.c_str());
- _size+=size;
- return *this;
- }
- string& append(size_t n, char c)
- {
- if(_size + n > _capacity)
- {
- reserve(_size + n);
- }
- for(size_t i = 0; i < n; ++i)
- {
- _str[_size+i] = c;
- }
- _size+=n;
- return *this;
- }
- // string& append(const string& s); this->append(s.c_str());
- // string& append(size_t n, char c); push_back(c);
- string& operator+=(char c)
- {
- this->push_back(c);
- return *this;
- }
- string& operator+=(const char* s)
- {
- this->append(s);
- return *this;
- }
- string& operator+=(const string& s)
- {
- this->append(s);
- return *this;
- }
- string& insert(size_t pos, char c)
- {
- assert(pos <= _size);
- if(_size == _capacity)
- {
- reserve(_capacity == 0?4:2*_capacity);
- }
- // int end = _size;
- // while(end >= (int)pos)
- // {
- // _str[end+1] = _str[end];
- // end--;
- // }
- // 比较推荐的写法,主要是 int和size_t比较,会出现比较错误,当int小于0时。
- size_t end = _size+1;
- while(end > pos)
- {
- _str[end] = _str[end-1];
- end--;
- }
- _str[pos] = c;
- ++_size;
- return *this;
- }
- string& insert(size_t pos, size_t n, char ch)
- {
- assert(pos <= _size);
- // pos==0 n==0 则下面while会出错(size_t)
- if(n == 0)
- return *this;
- if(_size + n > _capacity)
- {
- reserve(_size+n);
- }
- // [pos, pos+n) n个 [_size, _size+n) n个
- size_t end = _size + n;
- while(end >= pos+n)
- {
- _str[end] = _str[end-n];
- end--;
- }
- for(size_t i = pos; i < pos+n; ++i)
- {
- _str[i] = ch;
- }
- _size+=n;
- return *this;
- }
- string& insert(size_t pos, const char* str)
- {
- assert(pos<=_size);
- if(strlen(str) == 0)
- return *this;
- int len = strlen(str);
- if(_size+len > _capacity) {
- reserve(_size+len);
- }
- size_t end = _size+len;
- while(end >= pos+len)
- {
- _str[end] = _str[end-len];
- end--;
- }
- // int end = _size;
- // while(end >= (int)pos)
- // {
- // _str[end+len] = _str[end];
- // end--;
- // }
- strncpy(_str+pos,str,len); // strcpy会把\0也copy进去。
- _size+=len;
- return *this;
- }
- string& insert(size_t n, const string& s)
- {
- this->insert(n,s.c_str());
- return *this;
- }
- 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
- {
- size_t i = pos + len;
- size_t p = pos;
- while(i<=_size)
- {
- _str[p] = _str[i];
- i++;
- p++;
- }
- _size -= len;
- }
- return *this;
- }
- size_t find(char c, size_t pos = 0) const
- {
- assert(pos < _size);
- for(size_t i = pos; i < size(); ++i) {
- if(_str[i] == c)
- return i;
- }
- return npos;
- }
- size_t find(const char* str, size_t pos = 0) const
- {
- assert(pos < _size);
- assert(str);
- const char* p = strstr(_str+pos, str);
- if(p == nullptr)
- return npos;
- else
- return p-_str;
- }
- size_t find(const string& s, size_t pos = 0) const
- {
- return find(s.c_str(), pos);
- }
- string substr(size_t pos, size_t len = npos) const
- {
- assert(pos < _size);
- if(len == npos || len + pos >= _size)
- {
- len = _size - pos;
- }
- string ret;
- for(size_t i = pos; i < pos + len; ++i)
- {
- ret+=_str[pos];
- }
- return ret;
- }
- bool operator> (const string& s) const
- {
- return strcmp(_str, s.c_str())>0;
- }
- bool operator==(const string& s) const
- {
- return strcmp(_str, s.c_str()) == 0;
- }
- bool operator >=(const string& s)const
- {
- return *this > s || *this == s;
- }
- bool operator < (const string& s)const {
- return !(*this >= s);
- }
- bool operator <= (const string& s)const {
- return !(*this>s);
- }
- bool operator != (const string& s)const {
- return !(*this == s);
- }
- private:
- char* _str;
- size_t _size;
- size_t _capacity;
- public:
- const static size_t npos = -1;
- // static size_t npos;
- };
-
- ostream& operator<<(ostream& os, const string& s)
- {
- for(size_t i = 0; i < s.size(); ++i)
- os<
- return os;
- }
- // istream& operator>>(istream& is, string& s)
- // {
- // s.clear();
- // char ch;
- // ch = is.get();
- // while(ch != ' ' && ch != '\n')
- // {
- // s += ch;
- // ch = is.get();
- // }
- // return is;
- // }
- istream& operator>>(istream& is, string& s)
- {
- s.clear();
- char ch;
- ch = is.get();
-
- const int sz = 32;
- char buff[sz];
- size_t i = 0;
- while(ch != ' ' && ch != '\n')
- {
- buff[i] = ch;
- i++;
- if(i == sz-1)
- {
- buff[i] = '\0';
- s += buff;
- i = 0;
- }
- ch = is.get();
- }
- buff[i] = '\0';
- s+=buff;
- return is;
- }
- // size_t string::npos = -1;
- }
-
-
-
-
- #endif //STL_STRING_H
-
相关阅读:
2021全国大学生电子设计竞赛论文(智能送药小车(F题))(电赛论文模板)
微服务开发与实战Day01 - MyBatisPlus
资料库的webrtc文件传输
【代码随想录】栈与队列专栏(java版本)
10个必备的 async/await 工具函数
引用Geoip实现由IP地址获取国家城市等信息
vue模板语法上集
Java 使用 poi 和 aspose 实现 word 模板数据写入并转换 pdf 增加水印
强化学习问题(一)--- 输入conda activate base无法激活虚拟环境
[C/C++] 数据结构 链表OJ题:相交链表(寻找两个链表的相交起始结点)
-
原文地址:https://blog.csdn.net/i777777777777777/article/details/126435973