一、部分代码展示
#pragma once
#include<cstring>
#include<cassert>
#include<iostream>
using namespace std;
namespace bit
{class string{public:// 迭代器类指针// 范围for就是在编译时替换成迭代器遍历,*it返回给chtypedef 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;}/*string():_str(new char[1]),_size(0),_capacity(0){_str[0] = '\0';}*/string(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}// 拷贝构造要深拷贝,防止指针指向同一个空间/*string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}*/// 现代写法string(const string& s){string tmp(s._str);swap(tmp);}/*string& operator=(const string& 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){string ss(s);swap(ss);return *this;}*/// 现代写法// 去掉引用就是传值传参,s是拷贝,修改不会改变外面的值string& operator=(string s){swap(s);return *this;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}const char* c_str() const{return _str;}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){if (n > _capacity){char* tmp = new char[n];strcpy(tmp, _str);delete[] tmp;_str = tmp;_capacity = n;}}void push_back(char ch){/*if (_size == _capacity){reserve(capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_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的\0也拷过去//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;}void insert(size_t pos, char ch){assert(pos <= _size); // 等于就是尾插if (_size == _capacity){reserve(capacity == 0 ? 4 : 2 * _capacity);}//int end = _size; // 防止头插size_t减到负数变特别大的正数//while (end >= (int)pos)//{// _str[end + 1] = _str[end];// end--;//}int end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;}void insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size + len;while (end - len >= pos){_str[end] = _str[end - len];end--;}strcpy(_str + _size, str);_size += len;}void 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;}}void resize(size_t n, char c = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i < n; i++)_str[i] = c;_str[n] = '\0';_size = n;}}/* 库里面的swap是三次拷贝一次析构,消耗大编译器找函数的原则:1.编译器指挥向上找2.先在局部找,再在全局找,也可以在命名空间找3.指定了命名空间也在命名空间找string库里面也实现了两个参数的全局swap,目的就是为了区分算法库swap有现成用现成,所以有3个swap也是调全局swap*/void swap(string& str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}size_t find(char ch, size_t pos = 0) const{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;}size_t find(const char* sub, size_t pos = 0) const{assert(pos < _size);const char* p = strstr(_str + pos, sub);if (p)return p - _str;elsereturn pos;}string substr(size_t pos = 0, size_t len = npos){string sub;if (len == npos || len >= _size - pos)for (size_t i = pos; i < _size; i++)sub += _str[i];elsefor (size_t i = pos; i < pos + len; i++)sub += _str[i];return sub;}void clear(){_size = 0;_str[_size] = '\0';}~string(){delete[] _str;_size = 0;_capacity = 0;}private:char* _str;size_t _capacity;size_t _size;public:static const int npos;};const int string::npos = -1;void swap(string& x, string& y){x.swap(y);}// 重载比较函数可以是全局的,这样第一个参数就可以不是string,更灵活bool operator==(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;}bool operator<(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}// 流提取的重载必须全局,但不一定友元// 友元的目的就是为了获取类的私有成员变量进行打印ostream& operator<<(ostream& out, const string& s){for (auto ch : s)out << ch;return out;}//istream& operator>>(istream& in, string& s)//{// s.clear();// char ch;// // in >> ch; // istream默认不会读空格和换行符// ch = in.get(); // 相当于c语言里面的getchar()// while (ch != ' ' && ch != '\n')// {// s += ch;// ch = in.get();// }// return in;//}// 优化:缓冲区istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();char buf[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}ch == in.get();}if (i != 0){buf[i] = '\0';s += buf;}return in;}//istream& getline(istream& in, string& s)//{// s.clear();// char ch;// // in >> ch; // istream默认不会读空格和换行符// ch = in.get(); // 相当于c语言里面的getchar()// while (ch != '\n')// {// s += ch;// ch = in.get();// }// return in;//}// 优化istream& getline(istream& in, string& s){s.clear();char ch;ch = in.get();char buf[128];size_t i = 0;while (ch != '\n'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}ch == in.get();}if (i != 0){buf[i] = '\0';s += buf;}return in;}
}
二、细节
1、成员变量
要存储一个 string 对象需要有起始地址 _str,string 对象大小 _size,string 对象容量 _capacity
2、构造函数
(1)构造函数 string(const char* str = "")
string(const char* str = ""):_size(strlen(str))
{_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);
}
这个构造函数是深拷贝开空间
(2)拷贝构造 string(const string& s)
/*string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}*/
// 现代写法
string(const string& s)
{string tmp(s._str);swap(tmp);
}
拷贝构造的参数是不能改变的,所以用一个局部变量记录 s 信息再交换,最后除了函数 tmp 带着 *this 的信息销毁。一定要深拷贝,不然多个指针指向同一个空间,析构多次报错。
3、重载 = 运算符 string& operator=(string& s)
/*string& operator=(const string& 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)
{string ss(s);swap(ss);return *this;
}*/
// 现代写法
// 去掉引用就是传值传参,s是拷贝,修改不会改变外面的值
string& operator=(string s)
{swap(s);return *this;
}
三种写法
第一种,自己深拷贝
第二种,在函数内部用拷贝构造 + 交换
第三种,去掉 const 和 & 在传参的过程中拷贝构造临时变量 s,函数内部直接交换
4、交换函数 void swap(string& str)
void swap(string& str)
{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}
用于外部交换两个 string 对象,也用于内部构造对象
库里面的swap是三次拷贝一次析构,消耗大
编译器找函数的原则:
1.编译器指挥向上找
2.先在局部找,再在全局找,也可以在命名空间找
3.指定了命名空间也在命名空间找
string库里面也实现了两个参数的全局swap,目的就是为了区分算法库swap
有现成用现成,所以有3个swap也是调全局swap
5、迭代器 iterator
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;
}
string 的迭代器可以用指针
6、插入函数 void insert(size_t pos, char ch)
void insert(size_t pos, char ch)
{assert(pos <= _size); // 等于就是尾插if (_size == _capacity){reserve(capacity == 0 ? 4 : 2 * _capacity);}//int end = _size; // 防止头插size_t减到负数变特别大的正数//while (end >= (int)pos)//{// _str[end + 1] = _str[end];// end--;//}int end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;
}
注意 size_t 类型减到负数会变成很大的值
7、比较运算符的重载
bool operator==(const string& s1, const string& s2)
{int ret = strcmp(s1.c_str(), s2.c_str());return ret == 0;
}bool operator<(const string& s1, const string& s2)
{int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;
}bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}
重载比较函数可以是全局的,这样第一个参数就可以不是string,更灵活
8、流提取 ostream& operator<<(ostream& out, const string& s)
ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s)out << ch;return out;
}
流提取的重载必须全局,但不一定友元
友元的目的就是为了获取类的私有成员变量进行打印
9、流插入 istream& operator>>(istream& in, string& s)
//istream& operator>>(istream& in, string& s)
//{
// s.clear();
// char ch;
// // in >> ch; // istream默认不会读空格和换行符
// ch = in.get(); // 相当于c语言里面的getchar()
// while (ch != ' ' && ch != '\n')
// {
// s += ch;
// ch = in.get();
// }
// return in;
//}// 优化:缓冲区
istream& operator>>(istream& in, string& s)
{s.clear();char ch;ch = in.get();char buf[128];size_t i = 0;while (ch != ' ' && ch != '\n'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}ch == in.get();}if (i != 0){buf[i] = '\0';s += buf;}return in;
}
istream默认不会读空格和换行符,所以用 istream 的函数 get() 来获取一个字符,包括空格换行