【C++】String类的实现
模拟实现Sting类
1. 创建自定义空间
namespace lyx
{//模拟实现string类class string{}
}
2. 确定类框架
string 实际上就是一个类,我们在使用时,实例化这个类,并且调用其中的函数。
我们将成员变量私有化,成员函数公有化,留作接口,供外部使用:
class string
{public://成员函数private:char* _str;size_t _size;size_t _capacity;//npos 是一个常数,用来表示不存在的位置(参考string库的设置)const static size_t npos = -1;
}
3. 细化成员函数
3.1 重载 [ ]
字符串的底层实际就是一个字符数组,既然是数组,我们就可以通过以下方式去访问:
string a="hello world"cout<<a[1]; //e
因此要实现这种访问方式,就要进行运算符重载:
//重载[]//(1)普通对象:可读可写char& operator[](size_t pos){assert(pos < _size);return _str[pos];}//(2)const对象:只读char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}
3.2 构造函数和析构函数
想对字符串进行增删查改,得先另外开辟一段空间(在堆上),再将str指向的字符串拷贝到这段空间:
string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity+1];strcpy(_str, str);}
析构函数:
~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}
3.3 迭代器
string迭代器是指针,因为字符串是连续的地址空间。但不是所有的迭代器都是指针,因为不是所有的容器都是连续的。
typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}
对于某些函数(例如printf函数),我们希望对字符串只读,因此会调用const迭代器
typedef const char* const_itertor;const_itertor begin() const{return _str;}const_itertor end() const{return _str + _size;}
3.4 拷贝构造(重点理解)
首先由类和对象的知识,拷贝构造函数一定是引用返回。但默认生成的拷贝构造函数是浅拷贝,会导致S1和S2对象指向同一块地址空间,在先释放掉S2所指向空间后,再调用析构函数释放S1的空间,这段空间已经不存在了,程序会崩溃。因此需要自定义一个拷贝构造函数。
即先给S2开辟一段新的空间,再将S1拷贝给S2
传统写法:
//s2(s1)string(const string& s){_str = new char[s._capacity+1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);}
这里需要注意:拷贝构造函数的参数只有一个且必须是类类型对象的引用。 使用传值方式编译器直接报错,因为会引发无穷递归调用。同时为了防止S1引用被改变而导致s1的改变,还加了const。
现代写法:
string(const string& s):_str(nullptr), _size(0), _capacity(0){string tmp(s._str);swap(tmp);}
即先创建一个对象tmp,调用构造函数,使用s._str去初始化tmp,也就是_str开辟的空间的内容与s相同。
这个时候 tmp指向的空间就是 s2 想要的,所以我用swap 将s2._st(也就是this ->_str),和tmp._str交换,此时s2就指向了tmp原来指向的空间,效果上实现了深拷贝。
此时,tmp就指向了s2._str原来指向的空间,而s2在之前被我们初始化为nullptr了,所以在之后析构tmp的时候不会报错(delete nullptr 是被允许的)。
3.5 赋值运算符重载
将S3赋值给S1,可能会出现两种情况:1.S1长度小于S2,把S2给S1会造成越界;2.S1长度大于S2,把S2给S1会造成资源浪费。
//s1=s3string& 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;}}
因此,先开辟一段空间,再将S3的数据拷贝到S1指向的新空间中。再释放S1原来的空间。
也可以通过swap函数实现,思想与拷贝构造函数类似。
3.6 reserve
void reserve(size_t n){if (n > _capacity){char* tmp = new char[n+1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
3.7 resize
resize 和 reserve 的区别在于在开辟空间的时候会对空间初始化。所以我们需要在形参列表中加上一个char型字符,用来填充我们新开的空间。
当然,如果n小于_size,我们要将字符串截取到n长度
void resize(size_t n, char ch = '\\0'){if (n > _size){reserve(n);for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\\0';}else{_str[n] = '\\0';_size = n;}}
3.8 push_back
void push_back(char ch){if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] = ch;++_size;_str[_size] = '\\0';}
3.9 append
void append(const char* str){size_t len = strlen(str);if (_size + len < _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}
3.10 insert
插入一个字符
string& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}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){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;}
3.10 erase
string& erase(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= pos;}return *this;}
3. 整体代码
string.h
#pragma once
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;namespace lyx
{class string{public://--------------------------------------iterators------------------------------------//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;}//---------------------------member functions-------------------------------////' '单引号表示单个字符,这里表示转义字符终止符//" "双引号表示字符串常量//空字符常量使用转义符号 '\\0'表示,空白字符串使用双引号表示 ""//无论"\\0"和'\\0'都会转义,除非"a\\\\0a" -- a\\0a//构造函数/*string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity+1];strcpy(_str, str);}*/string(const char* str = ""){int len = strlen(str);_size = _capacity = len; //capacity记录有效字符的个数_str = new char[_capacity + 1]; //留一个空间给'\\0'strcpy(_str, str);}// s2(s1)// 传统写法//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);swap(tmp);}//s1=s3//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;// }//}//赋值重载--现代写法1//string& operator=(const string& s)//{// if (this == &s) //检查自我赋值// return *this;// string tmp(s);// swap(tmp);// return *this;//}//赋值重载--现代写法2string& operator=(string s){swap(s);return *this;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}//---------------------------------------Capacity--------------------------------------//size_t size() const{return _size;}size_t capacity() const{return _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 resize(size_t n, char ch = '\\0'){if (n > _size){reserve(n);for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\\0';}else{_str[n] = '\\0';_size = n;}}void clear(){_size = 0;_str[_size] = '\\0';}bool empty() const{return _size == 0;}//---------------------------------------Modify----------------------------------------////重载[]//(1)普通对象:可读可写char& operator[](size_t pos){assert(pos < _size);return _str[pos];}//(2)const对象:只读char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}void push_back(char ch){if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] = ch;++_size;_str[_size] = '\\0';}void pop_back(){_size--;_str[_size] = '\\0';}void append(const char* str){size_t len = strlen(str);if (_size + len < _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}//+=一个字符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 == _capacity){size_t newCapacity = _capacity == 0 ? 5 : _capacity * 2;reserve(newCapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[end] = ch;++_size;return *this;}//插入一个字符串string& insert(size_t pos, const char* str){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(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= pos;}return *this;}//交换两个字符串void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//----------------------------------------String operations-----------------------------//const char* c_str() const{return _str;}size_t find(char ch, size_t pos = 0) const{assert(pos < _size);while (pos < _size){if (_str[pos] == ch){return pos;}++pos;}return npos;}size_t find(const char* str, size_t pos = 0) const{assert(pos < _size);//strstr函数搜索一个字符串在另一个字符串中的第一次出现const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;}}//---------------------------------Non-member function overloads----------------------------//friend ostream& operator<<(ostream& out, const string& s);friend istream& operator>>(istream& in, string& s);friend istream& getline(istream& in, string& s);private:char* _str;size_t _size;size_t _capacity;const static size_t npos = -1;};//-----------------方便自定义类型打印---------------------////流插入重载inline ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s._size; ++i){out << s._str[i];}return out;}//流提取重载 14--2:54inline istream& operator>>(istream& in, string& s){s.clear();//char ch = in.get();//while (ch != ' ' && ch != '\\n')//cin遇到空格或\\n结束//{// s += ch;// ch = in.get();//}//优化char buff[128] = { '\\0' };size_t i = 0;char ch = in.get();while (ch != ' ' && ch != '\\n'){if (i == 127){s += buff;i = 0;}buff[i++] = ch;ch = in.get();}if (i > 0){buff[i] = '\\0';s += buff;}return in;}istream& getline(istream& in, string& s){char buff[128] = { '\\0' };size_t i = 0;char ch = in.get();while (ch != '\\n'){if (i == 127){s += buff;i = 0;}buff[i++] = ch;ch = in.get();}if (i > 0){buff[i] = '\\0';s += buff;}return in;}
}
Test.cpp
#define _CRT_SECURE_NO_WARNINGS#include "string.h"void Test_String1()
{//构造lyx::string s1;lyx::string s2("abcdef");//拷贝构造lyx::string s3 = s2;//赋值重载s1 = s2;
}void Test_String2()
{lyx::string s1("12345");lyx::string::iterator it = s1.begin();/*for (it; it < s1.end(); ++it)(*it)++;*/cout << s1.c_str() << endl;const lyx::string s2("67890");lyx::string::const_iterator it2 = s2.begin();/*for (it2; it2 < s2.end(); ++it2)(*it2)++;*/cout << s2.c_str() << endl;
}void Test_String3()
{lyx::string s("hello world");cout << s.c_str() << endl;cout << s.size() << endl;cout << s.capacity() << endl;s.resize(5);cout << s.size() << endl;cout << s.capacity() << endl;cout << s.c_str() << endl;s.reserve(15);cout << s.size() << endl;cout << s.capacity() << endl;cout << s.c_str() << endl;s.resize(20, 'x');cout << s.size() << endl;cout << s.capacity() << endl;cout << s.c_str() << endl;
}void Test_String4()
{/*lyx::string s = "hello world";s.push_back('!');cout << s.c_str() << endl;s.append("xxxxxxx");cout << s.c_str() << endl;s += '6';cout << s.c_str() << endl;s += "123456";cout << s.c_str() << endl;*/lyx::string s1 = "hello w";s1.insert(3,'x');cout << s1.c_str() << endl;s1.insert(2, "555");cout << s1.c_str() << endl;lyx::string s2 = "hello world";s2.erase(3, 3);cout << s2.c_str() << endl;
}void Test_String5()
{lyx::string s = ("abcefeefeefe");cout << s.find('f',3) << endl;cout << s.find('f') << endl;cout << s.find("efe", 3) << endl;cout << s.find("efe") << endl;
}void Test_String6()
{lyx::string s;//lyx::string s = ("hello world");cin >> s;cout << s << endl;
}int main()
{//Test_String1();//Test_String2();//Test_String3();//modify//Test_String4();//string operations//Test_String5();Test_String6();return 0;
}