赞
踩
模拟实现的节奏比较快,大家可以先去看看博主的关于string的使用,然后再来看这里的模拟实现过程
String模拟实现大致框架迭代器以及迭代器的获取(public定义,要有可读可写的也要有可读不可写的)/成员变量(private定义) 并且为了不和库的string冲突,我们需要自己搞一个命名空间
- namespace cyx
- {
- class string
- {
- public:
- //迭代器的实现(可读可写)
- typedef char* iterator;
- iterator begin()
- {
- return _str;
- }
- iterator end()
- {
- return _str + _size;
- }
- //迭代器的实现(可读不可写)
- typedef const char* const_iterator;
- const_iterator begin() const
- {
- return _str;
- }
- const_iterator end() const
- {
- return _str + _size;
- }
- private:
- char* _str;
- size_t _size;
- size_t _capacity;
- static const size_t npos;
- //static const size_t npos=-1 vs下int类型支持给static和const同时修饰的变量用缺省值
- };
- const size_t string::npos = -1;//静态成员遍历类外初始化
- }
nops是一个静态变量,要类内定义类外初始化,由于nops是size_t类型,赋值-1会被强转成最大的无符号整数
- //构造函数
- string(const char* str = "")
- :_size(strlen(str))
- {
- _capacity = _size == 0 ? 3 : _size;
- _str = new char[_capacity + 1];// 多开一块\0的空间
- strcpy(_str, str);
- }
1、 “ ”空字符串其实里面默认就有\0,所以缺省值直接给空字符串就行
2、_capacity 一定要给初始空间,不然后面如果涉及到2倍扩容,为0的话就扩不了了
3、要多开一块空间,连这个\0
4、可以复用strcpy函数
传统的思路就是拷贝,也就是我们先根据被拷贝的对象的_capacity开空间,然后再进行拷贝
- string(const string& s)
- :_size(s._size)
- , _capacity(s._capacity)
- {
- _str = new char[_capacity + 1];
- strcpy(_str, s._str);
- }
现代的思路就是,尝试去复用,比如说我们可不可以直接去利用前面的构造函数去构造一个新对象,然后再窃取新对象的成果(利用swap)
- //交换字符串
- 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)
- {
- string temp(s._str);
- swap(temp);
- }
传统写法和现代写法参数一样,不能重载,只能保留一个
- //迭代器区间构造
- template <class InputIterator>
- string(InputIterator first, InputIterator last)
- :_str(new char[last-first+1])
- ,_size(0)
- ,_capacity(last-first)
- {
- while (first != last)
- {
- push_back (*first);
- ++first;
- }
- }
这里定义的模版InputIterator的意思其实是这边我们可以传不同类型对象的迭代器,我们并不知道这个迭代器里面有多少元素,所以得用指针-指针,即last-first来确定我们的容量,然后再开空间,一个个进行尾插。
传统的思路就是,先开一块新空间拷贝旧数据,然后再释放掉原空间,这里尽量是先开空间再释放,避免我们开空间失败导致原始数据的丢失。
- //赋值重载(传统写法)
- string& operator=(const string& s)
- {
- if (this != &s)//避免自赋值
- {
- //先开新空间再毁旧空间,避免新空间开失败导致数据丢失
- char* temp = new char[s._capacity + 1];
- strcpy(temp, s._str);
- delete[]_str;
- _str = temp;
- _size = s._size;
- _capacity = s._capacity;
- }
- return *this;
- }
注意:要注意自赋值情况!!否则刚拷贝完自己就被释放了
现代的思路就是,既然被赋值这个空间不想要,那就和形参直接交换吧!!但是要注意的是,这里就不能像传统的一样用const引用了,否则不想要的空间就给到我们的赋值对象了,这边就得用传值传参,这样被交换的就只是一个临时拷贝,不想要的空间随着栈帧的结束被销毁。
- //赋值重载(现代写法)
- string& operator=(string s)//必须用值传递,否则会导致原数据的丢失
- {
- swap(s);
- return *this;
- }
传统写法和现代写法参数不一样,一个是const引用,一个传值传参,所以可以同时存在。
- ~string()
- {
- delete[]_str;
- _str = nullptr;
- _size = _capacity = 0;
- }
- //获取当前size
- size_t size() const
- {
- return _size;
- }
- //获取当前capacity
- size_t capacity() const
- {
- return _capacity;
- }
如果n比_capacity大,思路就是先开新空间进行拷贝,然后再释放旧空间
- //改变capacity
- void reserve(size_t n)
- {
- if (n > _capacity)
- {
- char* tmp = new char[n + 1];
- strcpy(tmp, _str);
- delete[] _str;
- _str = tmp;
- _capacity = n;
- }
- }
如果n比_size大,我们根据n去扩容,然后因为string的底层是字符数组,所以memset就很适合,他就是可以去一个字节一个字节设置成我们想要的。缺省值给‘\0’
- //改变size
- void resize(size_t n, char ch = '\0')
- {
- if (n > _size)
- {
- reserve(n);
- memset(_str + _size, ch, n - _size);
- }
- _size = n;
- _str[_size] = '\0';
- }
- //清理字符串
- void clear()
- {
- _str[0] = '\0';
- _size = 0;
- }
- //判断字符串是否为空
- bool empty()
- {
- return _capacity == 0;
- }
缩容到size位置,平时用的很少,我们要尽量减少扩容,思路也是一样的,开辟新空间去拷贝,再释放旧空间
- void shrink_to_fit()
- {
- char* temp = new char[_size + 1];
- strcpy(temp, _str);
- delete[] _str;
- _str = temp;
- _capacity = _size;
- }
- //[]重载(可读可写)
- char& operator[](size_t pos)
- {
- assert(pos < _size);//确保地址有效
- return _str[pos];
- }
- //[]重载(可读不可写)
- const char& operator[](size_t pos) const
- {
- assert(pos < _size);//确保地址有效
- return _str[pos];
- }
- //比较类型重载
- //>
- bool operator>(const string& s) const
- {
- return strcmp(_str, s._str) > 0;
- }
- //==
- bool operator==(const string& s) const
- {
- return strcmp(_str, s._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);
- }
有了[ ]、迭代器,我们可以展示3种遍历方法:下标访问、迭代器区间访问、范围for访问
- void Print(const string& s)
- {
- //下标遍历
- for (size_t i = 0; i < s.size(); ++i)
- cout << s[i] << " ";
- cout << endl;
- //迭代器遍历访问
- string::const_iterator it = s.begin();
- while (it != s.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- //范围for
- for (auto &e : s)
- cout << e << " ";
- cout << endl;
- }
字符串的增删接口一般要设置两个版本,一个是操作字符,一个是操作字符串,我们先把最难的insert和erase搞了,其他的就可以复用了
- //指定位置插入一个字符
- string& insert(size_t pos, char ch)
- {
- assert(pos <= _size);
- //判断是否需要扩容
- if (_size + 1 > _capacity)
- reserve(2 * _capacity);
- //pos后的数据要往后挪,所以要从后往前移
- 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* str)
- {
- assert(pos <= _size);
- //判断是否需要扩容
- size_t len = strlen(str);
- if (_size + len > _capacity)
- reserve(_size + len);
- size_t end = _size + len;
- while (end > pos + len - 1)
- {
- _str[end] = _str[end - len];
- --end;
- }
- //拷贝插入
- strncpy(_str + pos, str, len);
- _size += len;
- return *this;
- }
- //删除指定位置之后的字符
- string& erase(const size_t pos, size_t len = npos)
- {
- assert(pos <= _size);
- //有两种情况,一种是全删完,一种是删中间的一部分
- //全删完
- if (len == npos || pos + len > _size)//len == npos必须写,因为nops是无符号最大值,+的话会溢出
- {
- _str[pos] = '\0';
- _size = pos;
- }
- //删一部分
- else
- {
- strcpy(_str + pos, _str + pos + len);
- _size -= len;
- }
- return *this;
- }
len == npos这个判断条件必须写,因为nops已经是无符号最大值了,再+会溢出
- //尾插一个字符
- void push_back(char ch)
- {
- insert(_size, ch);
- }
- //尾插一个字符串
- string& append(const char* str)
- {
- return insert(_size, str);
- }
- //+=重载 字符
- string& operator+=(char ch)
- {
- push_back(ch);
- return *this;
- }
- //+=重载 字符串
- string& operator+=(const char* str)
- {
- append(str);
- return *this;
- }
- //获取c类型字符串
- const char* c_str() const
- {
- return _str;
- }
- //寻找指定字符串并返回下标
- size_t find(const char* str, size_t pos = 0)const
- {
- assert(pos < _size);
- char* p = strstr(_str + pos, str);
- if (p == nullptr)
- return npos;
- return p - _str;
- }
我们不能写在类内,否则会*this会占用第一个操作数,不符合我们的使用习惯。
- //重载<<
- std::ostream& operator<< (std::ostream& out, const string& s)
- {
- //可以用范围for,也可以用迭代器 范围for是用宏写的,本质上也是迭代器!
- for (auto ch : s)
- out << ch;
- return out;
- }
首先我们要知道两点,1.>>只会读取到空格或者换行结束 2.读取前会清理掉原空间的数据
- //重载>>
- std::istream& operator>> (std::istream& in, string& s)
- {
- //读取前要先清理掉原来存在的字符
- s.clear();
- //用get获取字符
- char ch = in.get();
- //先用一个数组存起来,再一起加
- char buff[128];
- size_t i = 0;
- while (ch != ' ' && ch != '\n')
- {
- //原始方法,一个字符一个字符加太麻烦,先用一个数组存起来,再一起加
- //s += ch;
- buff[i++] = ch;
- if (i == 127)
- {
- buff[127] = '\0';
- s += buff;
- i = 0;//重置i
- }
- ch = in.get();
- }
- //循环结束后可能还要一些字母没有存进去
- if (i != 0)
- {
- buff[i] = '\0';
- s += buff;
- }
- return in;
- }
我们用一个buff数组来暂时存储需要插入的字符,等存完了再+=,这样可以提高效率,以空间换时间
- namespace cyx
- {
- using std::endl;
- using std::cout;
- class string
- {
- public:
- //迭代器的实现(可读可写)
- typedef char* iterator;
- iterator begin()
- {
- return _str;
- }
- iterator end()
- {
- return _str + _size;
- }
- //迭代器的实现(可读不可写)
- typedef const char* const_iterator;
- const_iterator begin() const
- {
- return _str;
- }
- const_iterator end() const
- {
- return _str + _size;
- }
- //交换字符串
- void swap(string& s)
- {
- std::swap(_str, s._str);//浅拷贝,没有开空间,只是改变指针指向
- std::swap(_size, s._size);
- std::swap(_capacity, s._capacity);
- }
- //构造函数
- string(const char* str = "")
- :_size(strlen(str))
- {
- _capacity = _size == 0 ? 3 : _size;
- _str = new char[_capacity + 1];// 多开一块\0的空间
- strcpy(_str, str);
- }
- //拷贝构造函数(传统写法)
- /*string(const string& s)
- :_size(s._size)
- , _capacity(s._capacity)
- {
- _str = new char[_capacity + 1];
- strcpy(_str, s._str);
- }*/
- //拷贝函数的现代写法
- string(const string& s)
- :_str(nullptr)
- {
- string temp(s._str);
- swap(temp);
- }
- //迭代器区间构造
- template <class InputIterator>
- string(InputIterator first, InputIterator last)
- :_str(new char[last-first+1])
- ,_size(0)
- ,_capacity(last-first)
- {
- while (first != last)
- {
- push_back (*first);
- ++first;
- }
- }
- //赋值重载(传统写法)
- string& operator=(const string& s)
- {
- if (this != &s)//避免自赋值
- {
- //先开新空间再毁旧空间,避免新空间开失败导致数据丢失
- char* temp = new char[s._capacity + 1];
- strcpy(temp, s._str);
- delete[]_str;
- _str = temp;
- _size = s._size;
- _capacity = s._capacity;
- }
- return *this;
- }
- //赋值重载(现代写法)
- string& operator=(string s)//必须用值传递,否则会导致原数据的丢失
- {
- swap(s);
- return *this;
- }
- //析构函数
- ~string()
- {
- delete[]_str;
- _str = nullptr;
- _size = _capacity = 0;
- }
- //获取c类型字符串
- const char* c_str() const
- {
- return _str;
- }
- //获取当前size
- size_t size() const
- {
- return _size;
- }
- //获取当前capacity
- 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];
- }
- //比较类型重载
- //>
- bool operator>(const string& s) const
- {
- return strcmp(_str, s._str) > 0;
- }
- //==
- bool operator==(const string& s) const
- {
- return strcmp(_str, s._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);
- }
- //改变size
- void resize(size_t n, char ch = '\0')
- {
- if (n > _size)
- {
- reserve(n);
- memset(_str + _size, ch, n - _size);
- }
- _size = n;
- _str[_size] = '\0';
- }
- //改变capacity
- void reserve(size_t n)
- {
- if (n > _capacity)
- {
- char* tmp = new char[n + 1];
- strcpy(tmp, _str);
- delete[] _str;
- _str = tmp;
- _capacity = n;
- }
- }
- //尾插一个字符
- void push_back(char ch)
- {
- insert(_size, ch);
- }
- //尾插一个字符串
- string& append(const char* str)
- {
- return insert(_size, str);
- }
- //+=重载 字符
- string& operator+=(char ch)
- {
- push_back(ch);
- return *this;
- }
- //+=重载 字符串
- string& operator+=(const char* str)
- {
- append(str);
- return *this;
- }
- //指定位置插入一个字符
- string& insert(size_t pos, char ch)
- {
- assert(pos <= _size);
- //判断是否需要扩容
- if (_size + 1 > _capacity)
- reserve(2 * _capacity);
- //pos后的数据要往后挪,所以要从后往前移
- 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* str)
- {
- assert(pos <= _size);
- //判断是否需要扩容
- size_t len = strlen(str);
- if (_size + len > _capacity)
- reserve(_size + len);
- size_t end = _size + len;
- while (end > pos + len - 1)
- {
- _str[end] = _str[end - len];
- --end;
- }
- //拷贝插入
- strncpy(_str + pos, str, len);
- _size += len;
- return *this;
- }
- //删除指定位置之后的字符
- string& erase(const size_t pos, size_t len = npos)
- {
- assert(pos <= _size);
- //有两种情况,一种是全删完,一种是删中间的一部分
- //全删完
- if (len == npos || pos + len > _size)//len == npos必须写,因为nops是无符号最大值,+的话会溢出
- {
- _str[pos] = '\0';
- _size = pos;
- }
- //删一部分
- else
- {
- strcpy(_str + pos, _str + pos + len);
- _size -= len;
- }
- return *this;
- }
- //寻找指定字符并返回下标
- 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* str, size_t pos = 0)const
- {
- assert(pos < _size);
- char* p = strstr(_str + pos, str);
- if (p == nullptr)
- return npos;
- return p - _str;
- }
- //清理字符串
- void clear()
- {
- _str[0] = '\0';
- _size = 0;
- }
- //缩容到他的size
- void shrink_to_fit()
- {
- char* temp = new char[_size + 1];
- strcpy(temp, _str);
- delete[] _str;
- _str = temp;
- _capacity = _size;
- }
- //判断字符串是否为空
- bool empty()
- {
- return _capacity == 0;
- }
- private:
- char* _str;
- size_t _size;
- size_t _capacity;
- static const size_t npos;
- //static const size_t npos=-1 vs下int类型支持给static和const同时修饰的变量用缺省值
- };
- const size_t string::npos = -1;//静态成员遍历类外初始化
- //重载<<
- std::ostream& operator<< (std::ostream& out, const string& s)
- {
- //可以用范围for,也可以用迭代器 范围for是用宏写的,本质上也是迭代器!
- for (auto ch : s)
- out << ch;
- return out;
- }
- //重载>>
- std::istream& operator>> (std::istream& in, string& s)
- {
- //读取前要先清理掉原来存在的字符
- s.clear();
- //用get获取字符
- char ch = in.get();
- //先用一个数组存起来,再一起加
- char buff[128];
- size_t i = 0;
- while (ch != ' ' && ch != '\n')
- {
- //原始方法,一个字符一个字符加太麻烦,先用一个数组存起来,再一起加
- //s += ch;
- buff[i++] = ch;
- if (i == 127)
- {
- buff[127] = '\0';
- s += buff;
- i = 0;//重置i
- }
- ch = in.get();
- }
- //循环结束后可能还要一些字母没有存进去
- if (i != 0)
- {
- buff[i] = '\0';
- s += buff;
- }
- return in;
- }
-
- //遍历方法的展示
- void Print(const string& s)
- {
- //下标遍历
- for (size_t i = 0; i < s.size(); ++i)
- cout << s[i] << " ";
- cout << endl;
- //迭代器遍历访问
- string::const_iterator it = s.begin();
- while (it != s.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- //范围for
- for (auto &e : s)
- cout << e << " ";
- cout << endl;
- }
有新的后面再补充哦!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。