当前位置:首页 » 《关于电脑》 » 正文

【C++】string类 (模拟实现详解 上)

5 人参与  2024年10月29日 10:00  分类 : 《关于电脑》  评论

点击全文阅读


        我们不仅要会使用strng的接口,还要模拟实现,更深地理解strng的底层逻辑。这里我们最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数这些比较核心的接口。

1.准备工作

        我们依旧采用声明和定义分离的方式模拟实现string,跟之前模拟实现Stack、顺序表那些是同样的操作,建三个文件,一个头文件string.h,两个源文件test.cpp和string.cpp。

string.h中用命名空间分隔一下,因为c++库里面也有string,避免冲突。string.h里面写一些会用到的头文件,一个string类,string类的成员变量都是老朋友了,size和capacity也是介绍过的,和模拟实现顺序表差不多的,前面的博文说过,string可以认为是char类型的顺序表。

#include <iostream>#include <assert.h>using namespace std;namespace lyj //用命名空间与库里的string分隔开{class string{private:char* _str;size_t _size;size_t _capacity;};}

2.基础接口

短小而且频繁被调用的函数,就直接放在string类里面,就不做声明和定义分离了。

2.1 无参构造

还是写在string.h里的string类,作为string类的成员函数。

class string{public:string() //无参构造:_str(new char[1]{'\0'}) //不能是nullptr,_size(0),_capacity(0){}private:char* _str;size_t _size;size_t _capacity;};

无参构造走初始化列表,一个一个初始化,不传参_size和_capacity都是0,但是_str不可以为nullptr,这里要开辟一个空间,初始化为'\0'。

2.2 带参构造

写在string类里面。带参构造就不要走初始化列表初始化了,因为不是很方便,我们就在函数体里实现。

class string{public:string() //无参构造:_str(new char[1]{'\0'}),_size(0),_capacity(0){}string(const char* str) //带参构造{_size = strlen(str);_capacity = _size; //capacity大小不包括\0_str = new char[_capacity + 1]; //开空间时多开一个strcpy(_str, str);//拷贝 }private:char* _str;size_t _size;size_t _capacity;};

传参的时候_size就是传过来的str的大小,_capacity也初始化的和size一样大,切记,_capacity大小是不包括'\0'的,但是我们开空间的时候要多开一个,所以new char的大小是_capacity+1。空间开好之后就把str的数据拷贝到_str去,就初始化好了。

test.cpp中测试一下带参构造和无参构造。

#include "string.h"namespace lyj //命名空间保持一致{void test1(void){string s1; //不传参string s2("hello world");//传参}}int main(){lyj::test1(); //指定命名空间调用函数return 0;}

2.3 无参构造和带参构造结合

两种构造函数可以结合成一个构造函数,如下。

string(const char* str = "") //给缺省值,什么都没有的字符串{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);//拷贝 }

什么都没有的字符串自带\0,strlen求的是\0之前的字符长度,如果不传参,strlen(str)的结果就是缺省值的空字符串大小,为0, _capacity = _size = 0,new的大小为1;如果传参就是带参构造的结果。

test.cpp中测试一下这个结合的构造函数。

#include "string.h"namespace lyj //命名空间保持一致{void test1(void){string s1; //不传参string s2("hello world");//传参}}int main(){lyj::test1(); //指定命名空间调用函数return 0;}

结果是正确的。 

2.3 析构函数

写在string类里面。

~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}

 析构函数没啥多说的。

2.5 c_str

写在string类里面。这个接口也算是打印函数,我们还没实现流插入流提取函数,暂时用这个。

const char* c_str(){return _str;}

test.cpp中测试一下。

namespace lyj //命名空间保持一致{void test1(void){string s1; //不传参string s2("hello world");//传参        //打印cout << s1.c_str() << endl;cout << s2.c_str() << endl;}}int main(){lyj::test1(); //指定命名空间调用函数return 0;}

2.6 size 、capacity 和 operator[]

都写在string类里面。

size_t size() const{return _size;}size_t capacity() const{return _capacity;}

operator提供两个版本,普通对象和const对象。 普通对象返回引用,可以修改,const对象返回const引用,不可修改。

char& operator[](size_t pos) //普通对象{assert(pos < _size);//断言,防止越界return _str[pos];}const char& operator[](size_t pos) const //const对象{assert(pos < _size);//断言,防止越界return _str[pos];}

2.7 拷贝构造

还是写在string.h里的string类,作为string类的成员函数。

//假设用s1拷贝s2,即s2(s1)string(const string& s)//拷贝构造{_str = new char[s._capacity]+ 1; //s2开和s1一样的大小strcpy(_str, s._str);//拷贝数据_size = s._size;_capacity = s._capacity;}

 有资源的申请是深拷贝。

2.8 operator=  赋值

还是写在string.h里的string类,作为string类的成员函数。

//s2 = s1;string operator=(const string& s) //赋值{delete[] _str;_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;return *this;}

但是这个代码,自己赋值给自己就会出问题。所以我们要做一个改动。

string operator=(const string& s) //赋值{if (this != &s) //不是自己给自己赋值时{delete[] _str;_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;}return *this;}

 

3.迭代器和范围for

先实现迭代器。

typedef char* iterator; //给char*换个名字叫iteraatoriterator begin(){return _str;}iterator end(){return _str + _size;//_str + _size是'\0'的位置}

但是不可以认为迭代器就是指针,没有这么简单。 只能在string里面用指针这样实现一下。

test.cpp里测试。

namespace lyj //命名空间保持一致{void test1(void){string s1; //不传参string s2("hello world");//传参//迭代器遍历string::iterator it = s2.begin();while (it != s2.end()){cout << *it << " ";++it;}cout << endl;}}int main(){lyj::test1(); //指定命名空间调用函数return 0;}

 范围for可以直接用,因为我们说过,范围for其实底层就是迭代器。

如果我们现在把迭代器的实现注释掉,范围for也不能用。

除了普通迭代器,还有const迭代器。

typedef const char* const_iterator; //给const char*换个名字叫const_iteraatorconst_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}

到这我们就大概实现了string的大概框架,增删查改的接口我们下次再说,本篇就到这里,拜拜~


点击全文阅读


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

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

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

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

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