当前位置:首页 » 《资源分享》 » 正文

C++——string的模拟实现(上)

16 人参与  2024年10月30日 15:21  分类 : 《资源分享》  评论

点击全文阅读


目录

引言

成员变量

1.基本框架

成员函数

1.构造函数和析构函数

2.拷贝构造函数

3.容量操作函数

3.1 有效长度和容量大小

3.2 容量操作

3.3 访问操作

(1)operator[]函数

(2)iterator迭代器

3.4 修改操作

(1)push_back()和append()

(2)operator+=函数


引言

在 C++——string的了解和使用 中,我们学习了string的一些基础用法。接下来我们可以试着模拟实现string。

在C++中,std::string是一个功能强大且广泛使用的类,用于处理字符串。然而,了解其内部实现原理对于深入理解C++和编写高效代码至关重要。通过模拟实现一个简单的string类,我们可以更好地理解字符串的存储、管理以及操作。这不仅有助于我们更好地使用std::string,还能让我们在遇到特定需求时,能够自定义字符串类来满足这些需求。

成员变量

1.基本框架

为了与STL库中的string区分开来,我们要使用命名空间namespace进行封装。

char* _str:指向字符数组的指针,用于存储字符串的实际内容。

size_t  _size:表示字符串中有效字符的数量。

size_t  _capacity:表示字符数组的容量,即可以存储的最大字符数量(包括结尾的空字符\0)。

namespace My_string{    class string {    public:        // ...private:char* _str;size_t _size;size_t _capacity;};}

成员函数

老规矩,我们在 string.h 中,声明函数;在 string.cpp 中,实现函数的功能。

1.构造函数和析构函数

构造函数:接受一个C风格字符串作为参数,计算其长度,分配足够的内存来存储该字符串及其结尾的空字符,并复制字符串内容。

析构函数:释放分配给字符串的内存,并将指针设置为nullptr,以避免悬挂指针问题。同时,将_size和_capacity设置为0,表示对象已销毁。

string.h:

namespace My_string{    class string {    public:string(const char* str);//构造函数~string();//析构函数private:char* _str;size_t _size;size_t _capacity;};}

string.cpp:

#include"string.h"namespace My_string{// 构造函数    string::string(const char* str)    {    _size = strlen(str);    _capacity = _size;    _str = new char[_capacity + 1];    // +1用于储存'\0'    strcpy(_str, str);    }    // 析构函数    string::~string()    {    delete[] _str;    _str = nullptr;    _size = _capacity = 0;    }}

我们可以测试一下:

通过调试观察一下:

调用构造函数:

调用析构函数:

2.拷贝构造函数

拷贝构造函数:接受一个string对象作为参数,分配足够的内存来存储原对象的字符串内容,并复制该内容。

这里提供了三种实现方式,包括直接复制、使用临时对象进行深拷贝以及使用swap函数进行资源转移。

string.h:

string(const string& str);//拷贝构造函数

string.cpp:

// 拷贝构造函数(1)string::string(const string& str){_str = new char[str._capacity + 1];//额外多给一个空间,用于存放'/0'strcpy(_str, str._str);//拷贝数据_capacity = str._capacity;//设置容量_size = str._size;//设置有效数据个数}

我们在这里也有其他的方法可以实现拷贝构造:

// 拷贝构造函数(2)string::string(const string& str){string tmp(str._str);std::swap(tmp._str, _str);std::swap(tmp._size, _size);std::swap(tmp._capacity, _capacity);}

以上代码还可以接着简化:

string::string(const string& str){string tmp(str._str);swap(tmp);// 这里的swap我们接下来会定义}

3.容量操作函数

3.1 有效长度和容量大小

我们先写这两个函数:

size()和capacity():分别返回字符串的有效长度和字符数组的容量。

string.h:

size_t size() const;// size()函数size_t capacity() const;// capacity()函数

string.cpp:

// size()函数size_t string::size() const{return _size;}// capacity()函数size_t string::capacity() const{return _capacity;}
3.2 容量操作

c_str():返回一个指向以空字符结尾的字符数组的指针,该数组包含与string对象相同的字符序列。这允许将string对象与接受C风格字符串的函数一起使用。

empty():检查字符串是否为空(即长度为0)。

erase():删除字符串中指定位置的字符或子字符串。

string.h:

const char* c_str() const;// c_str()函数bool empty() const;// empty()函数void erase(size_t pos = 0, size_t len = npos);    // erase()函数

string.cpp:

// c_str()函数const char* string::c_str() const{return _str;}// empty()函数bool string::empty() const{return _size == 0;}// erase()函数void string::erase(size_t pos,size_t len){assert(pos < _size); if (len == npos || len >= _size - pos){_str[pos] = '\0';// 位置pos置为'\0'_size = pos;// 有效元素个数为pos个}else// len小于后面的字符个数{// 将后面的字符拷贝到pos位置strcpy(_str + pos, _str + pos + len);_size -= len;// 更新有效元素}}

接下来再来实现扩容函数reserve()和resize():

reserve():增加字符数组的容量,以确保可以存储至少n个字符。如果当前容量不足,则分配新的内存并复制现有内容。

resize():改变字符串的大小。如果新大小大于当前大小,则添加新字符(默认为\0);如果新大小小于当前大小,则删除多余的字符。

string.h:

// 预留空间void reserve(size_t n);// resize()函数void resize(size_t n, char ch = '\0');

string.cpp:

// 预留空间void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}// resize()函数void string::resize(size_t n, char ch){if (n > _size){if (n > _capacity){reserve(n);}// 使用 memset 函数将字符 ch // 填充到新添加的空间中memset(_str + _size, ch, n - _size);}_size = n;_str[n] = '\0';}
3.3 访问操作
(1)operator[]函数

operator[]函数的功能:返回pos位置的字符

string.h:

// 非const版本char& operator[](size_t pos);//operator[]函数// const版本const char& operator[](size_t pos)const;

string.cpp:

// operator[]函数char& string::operator[](size_t pos){assert(pos < _size);return _str[pos];}// const版本const char& string::operator[](size_t pos)const{assert(pos < _size);return _str[pos];}

来个简单的代码测试一下:

(2)iterator迭代器

迭代器:提供begin()和end()函数来返回指向字符串开头和结尾的迭代器。这里简化了迭代器的实现,将其视为指向字符数组的指针。然而,在实际应用中,迭代器通常是一个更复杂的类,提供了更多的功能和安全性检查。

string.h:

//const版本的iteratorconst_iterator begin() const;//提供const_iterator begin()函数const_iterator end() const;//提供const_iterator end()函数//非const版本的iteratoriterator begin();//提供iterator begin()函数iterator end();//提供iterator end()函数

string.cpp:

string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin() const{return _str;}string::const_iterator string::end() const{return _str + _size;}

还是老样子,我们使用一个简单的函数测试一下:

void test3(){My_string::string str("hello");for (auto i : str){cout << i << " ";}cout << endl;My_string::string::iterator it1 = str.begin();while (it1 != str.end()){cout << *it1 << " ";++it1;}cout << endl;My_string::string::iterator it2 = str.end();if (it2 != str.begin()) { // 检查避免直接解引用 end()  --it2; // 先移动到一个有效的位置  while (it2 != str.begin()){std::cout << *it2 << " ";--it2;}std::cout << *it2 << " "; // 输出最后一个字符(begin() 之前的字符)  }std::cout << std::endl;}

输出结果为:

3.4 修改操作
(1)push_back()和append()

string.h:

// 尾插一个字符void push_back(char ch);// 尾插一个字符串void append(const char* str);

string.cpp:

//尾插一个字符void string::push_back(char ch){if (_capacity == _size){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;_str[_size + 1] = '\0';_size++;}//尾插一个字符串void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity) {reserve(_size + len);}strcpy(_str+_size, str);_size += len;}
(2)operator+=函数

我们可以借助上面两个函数实现operator+=函数。

string.h:

//operator+=函数可以构成重载,函数名相同,参数不同string& operator+=(char ch);// 字符相加string& operator +=(const char* str);// 字符串相加

string.cpp:

string& string::operator+=(char ch){// 调用push_back()函数push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}

来测试一下:

operator+=函数返回的是对象本身。

内置类型的+=运算符返回值的副本。

自定义类型的+=运算符通常返回对象的引用(即*this),以支持链式操作和避免复制。

———————————————————————————————————————————

以上为string模拟实现的第一篇

求点赞收藏评论关注!!!

感谢各位大佬!!!

第二篇链接:C++——string的模拟实现(下)


点击全文阅读


本文链接:http://zhangshiyu.com/post/180038.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1