行业知识
从零开始:C++ String类的模拟实现
文章目录
- 引言
- 1.类的基本结构
- 2.构造函数和析构函数
- 3.基本成员函数
- 总结
引言
在C++编程中,字符串操作是非常常见且重要的任务。标准库中的std::string类提供了丰富且强大的功能,使得字符串处理变得相对简单。然而,对于学习C++的开发者来说,深入理解std::string的内部实现原理是非常有益的。通过亲手实现一个类似的String类,不仅可以帮助我们掌握面向对象编程的基本概念,还能增强我们对内存管理和字符串操作的理解。
在这篇博客中,我们将从零开始,逐步实现一个自定义的C++ String类。我们的目标是构建一个功能完整且高效的字符串类,同时尽可能地模仿std::string的行为。我们将讨论类的基本结构、构造函数和析构函数的实现、基本成员函数的设计、运算符重载、内存管理,以及如何编写测试代码来验证我们的实现。
通过这篇文章,您将学到如何在C++中进行动态内存分配和管理,如何实现深拷贝和移动语义,如何重载运算符以提升类的易用性,等等。无论您是刚刚入门的C++学习者,还是希望深入理解C++底层实现的开发者,这篇文章都将为您提供宝贵的知识和实践经验。
让我们一起来探索C++ String类的实现之旅吧!
1.类的基本结构
1.1定义类
#include#includeusing namespace std;namespace lyrics{class string{public:typedef char* iterator;typedef const char* const_iterator;//迭代器iterator begin();iterator end();const_iterator begin()const;const_iterator end()const;//构造函数string(const char* str = "");string(const string& s);//析构函数~string();//const char* c_str() const;//返回大小size_t size() const;//运算符重载char& operator[](size_t pos);const char& operator[](size_t pos)const;//空间扩容void reserve(size_t n);//尾插一个字符void push_back(char ch);//尾插一个字符串void append(const char* str);//运算符重载+=操作string& operator+=(char ch);string& operator+=(const char* str);//插入操作,插入一个字符串和插入一个字符void insert(size_t pos, char ch);void insert(size_t pos, const char* str);//删除某段字符void erase(size_t = 0, size_t len = npos);//查找某个字符串或者字符size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);//赋值拷贝string& operator=(const string& s);//交换函数void swap(string& s);//取子串string substr(size_t pos = 0, size_t = npos);//比较函数运算符重载bool operator=(const string& s)const;bool operator==(const string& s)const;//清理void clear();private:size_t _size;size_t _capacity;char* _str;const static size_t npos;};//非成员函数,,重载流插入和流提取istream& operator>>(istream& is, string& str);ostream& operator_str = new char[_size + 1];_capacity = _size;strcpy(_str, str);}char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}return _str;}delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}return _size;}assert(pos4.0删除某段字符串
注意:在声明中len的缺省参数给的是npos,当我的长度大于pos对应的后面对应的长度的时候,这时候就有多少删多少,所以我们需要判断一下,第一个if判断的就是判断我们删除的长度是否已经超过了后面的长度,如果超过了就直接进入第一个if删除后面的所有,也就是把pos位置置为\0,然后将_size更新,如果不是的话可以直接将pos+len位置的子字符串拷贝到pos位置之后
//从pos位置删除len个字符void string::erase(size_t pos, size_t len){assert(pos = _size - pos){_str[pos] = 'size_t string::find(char ch, size_t pos){for (size_t i = pos;i';_size = pos;}else{strcpy(_str + pos, _str + pos + len);//直接把后面的copy到前面_size -= len;}}4.1查找函数
- 查找单个字符
size_t string::find(const char* sub, size_t pos){const char* str = strstr(_str + pos, sub);return str - _str;}- 查找字符串
查找字符串的话可以直接用C语言的库函数进行查找
//深拷贝string::string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}4.2深拷贝
void string::swap(string& s){//内置类型交换代价更小std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}4.3交换函数
这里不用库里的交换函数因为库里的交换函数的效率太低了,我们可以简单看看库里交换函数的代码
这里可以看到库里的swap函数是直接拷贝构造一个零时的对象,然后进行两次赋值拷贝,这样做效率是极低的,因为是内置类型,两次赋值拷贝都会进行创建新空间,然后释放旧的空间,这样的成本是很大的,所以可以直接写一个swap对内置类型进行交换,直接交换两个指针的指向,还有size和capacity即可
bool string::operatorreturn strcmp(_str, s._str) (istream& is, string& str){str.clear();char ch = is.get();while (ch != ' '&& ch != '\n'){str += ch;}return is;}4.4取子串
string string::substr(size_t pos, size_t len){//检查pos是否合法assert(pos _size - pos){//直接取后面的子串string sub(_str + pos);//从pos位置开始进行拷贝构造!!!!//返回子串return sub;}else{//构造子串string sub;//预开辟空间sub.reserve(len);//循环拷贝for (size_t i = 0;i4.5比较函数operator的一系列重载
这里只需要重载两个即可,其他的只需要进行复用就够了,比较函数的重载可以直接调用C语言中的字符串比较函数
ostream& operatorfor (size_t i = 0;i
- 流提取
流提取也不用直接访问成员变量,流提取可以直接一个字符一个字符的访问,通过operator[]的重载访问,一个一个大打印
- 流提取
- 查找字符串
- 查找单个字符
下一篇:火影手游服务器怎么注销?