【string类的简单模拟实现】
创始人
2024-05-22 11:34:53
0

目录

1 类中成员变量的声明

2 迭代器

3 一些常用接口

4 六大默认函数

4.1 构造

4.2 拷贝构造

4.3 赋值运算符重载

4.4 析构

5 开空间&&增删查改

6 其他运算符重载


1 类中成员变量的声明

通过上一篇文章对string类的简单使用相信大家对于string类中成员变量已经很熟悉了,这里就不再多说了:

class string{private:char* _str;int _size;//当前有效数据个数int _capacity;//存储有效数据的最大容量,不包括'\0'static const size_t npos;typedef char* iterator;typedef const char* const_iterator;};

但是为了测试时避免与库里面的冲突我们就把该类放在一个独立的命名空间中:

namespace grm
{class string{private:char* _str;int _size;//当前有效数据个数int _capacity;//存储有效数据的最大容量,不包括'\0'static const size_t npos;typedef char* iterator;typedef const char* const_iterator;};
}

2 迭代器

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

3 一些常用接口

        size_t size()const     //无论是不是const对象都能够调用{return _size;}char* c_str()const{return _str;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos)const{assert(pos < _size);return _str[pos];}void clear()const{_str[0] = '\0';_size = 0;}size_t capacity()const{return _capacity;}bool empty()const{return _size == 0;}

4 六大默认函数

4.1 构造

       string(const char* s):_size(strlen(s)),_capacity(_size){_str = new char[_capacity + 1];//多开出一个空间用来存储'\0'strcpy(_str, s);}

但是我们发现如果是无参又该怎么办?

有人会说像这样特殊处理一下就好了:

        string():_str(new char[1]),_size(0),_capacity(0){_str[0] = '\0';}

这样处理是没有问题的,但是我们学过缺省参数,在这里能不能够给一个缺省值呢?缺省值又该给那个呢?

C++中是这样处理的:

        string(const char* s="")//给一个空字符串:_size(strlen(s)),_capacity(_size){_str = new char[_capacity + 1];//多开出一个空间用来存储'\0'strcpy(_str, s);}

缺省值给的是一个空字符串。

4.2 拷贝构造

传统写法:

        //传统写法string(const string& s):_size(s._size),_capacity(s._capacity){_str = new char[_capacity+1];strcpy(_str, s._str);}

这种写法就是自己手动开空间拷贝。

现代写法:我们发现上面我们已经实现好了默认的构造函数,我们可以直接用构造函数来解决

        void swap(string& s){std::swap(s._str, _str);std::swap(s._capacity, _capacity);std::swap(s._size, _size);}//现代写法string(const string& s):_str(nullptr)//必须要给初始值,否则交换后调用析构函数就会析构随机值会崩溃,_size(0),_capacity(0){string tmp(s._str);swap(tmp);}

其中需要注意的是使用这种方式用初始化列表将_str初始化成nullptr,不然交换后调用tmp的析构函数就会释放随机地址而出错。

4.3 赋值运算符重载

与拷贝构造一样,赋值运算符重载也分为传统版本和现代版本。

传统版本:

        //传统写法string& operator=(const string& s){if (this != &s){//这种方式如果new失败了抛异常就会有问题,我们并不想_str维护的空间被释放delete[] _str;_str = new char[s._capacity+1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}

大家可以从注释中看见传统写法都是要自己手动开空间拷贝,而且大家也发现了代码中还多了一个判断this是否与&s相等,否则当直接delete_str后由于s与_str指向的是同一块空间,再对s解引用就是典型的野指针问题了。而且还有一个问题就是当我们new空间失败时我们并不想_str被释放,所以可以用下面这种写法:

        //传统写法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;}return *this;}

而且大家发现没有这样写即使不用写上面的if判断程序依旧可以正常运行,但是加上判断当自己给自己赋值时可以减少拷贝。

现代写法 方法1:

        //现代写法 方法1:string& operator=(const string& s){if (this != &s) //这里加上判断条件在自己给自己赋值能够减少拷贝,不加也是没有问题的{string tmp(s);swap(tmp);}return *this;}

现代写法 方法2:

        //现代写法 方法2:string& operator=(string s){swap(s);return *this;}

这种方式就更加巧妙了,直接用了传值传参,而传值传参就是一个拷贝构造。

4.4 析构

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

5 开空间&&增删查改

void reserve(size_t n) //这里要求开n个有效空间,不包括'\0'{if (_capacity < n){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){if (n > _capacity){reserve(n);}memset(_str + _size, ch, n - _size);}_size = n;_str[_size] = '\0';}
        void push_back(char ch){if (_capacity == _size)reserve(_capacity == 0 ? 4 : _capacity * 2);_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char* s){int len = strlen(s);if (_size + len > _capacity)reserve(_size + len);strcpy(_str + _size, s);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+= (const char* s){append(s);return *this;}size_t find(char ch){for (size_t i = 0; i < _size; i++)if (_str[i] == ch)return i;return -1;}size_t find(const char* s, size_t pos=0){int i = pos, j = 0;//i是主串遍历,j是子串遍历while (i < _size && j < strlen(s)){if (s[j] == _str[i]){i++;j++;}else{i = i - j + 1;j = 0;}}if (j == strlen(s))return i - j;elsereturn -1;}
        string& insert(size_t pos, const char* s)//在pos位置插入,字符串从pos位置开始插{assert(pos <= _size);int len = strlen(s);if (_size + len > _capacity)reserve(_size + len);int end = _size + len - 1;int gap = _size - pos + 1;while (gap--){_str[end] = _str[end - len];end--;}strncpy(_str + pos, s, len);_str[_size + len] = '\0';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+1;}strcpy(_str + pos, _str + len + pos);_size -= len;return *this;}

这些代码我们之前上顺序表都已经比较详细的介绍了,大家可以参考参考。

我们可以自己测试一下来看看:

 

 可以发现是没有多大问题的。


6 其他运算符重载

        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 strcmp(_str, s._str) <= 0;}bool operator>=(const string& s)const{return strcmp(_str, s._str) >= 0;}bool operator!=(const string& s)const{return strcmp(_str, s._str) != 0;}ostream& operator<<(ostream& out, const string& s){//out << s.c_str();//能这么写吗? 不能,也许我们会在字符中间插入一个'\0'for (auto& e : s){out << e;}/*for (int i = 0; i < s.size(); i++){out << s[i];}*/return out;}istream& operator>>(istream& in, string& s){s.clear();//要加上这个,否则可能出错char ch = in.get();while (ch != '\0' && ch != '\n'){s += ch;ch = in.get();}return in;}

上面这几个比较可以自己重载成成员方法,当然也可以重载成全局函数。下面流提取运算符和流插入运算符我们在之前已经讲过了,里面需要注意的细节代码中都有注释。


有需要源码的老铁可以去博主的码云中获得:

nijhttps://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72https://gitee.com/monday-sky/text_cpp/commit/e4716c73bba49dd2bad8c86986d0cfe26beece72

相关内容

热门资讯

公司辞旧迎新晚会主持词串词   男:尊敬的各位领导、各位来宾,  女:亲爱的同事们  合:大家下午好!  男:光阴似箭,岁月如梭...
纯中式婚礼主持词 纯中式婚礼主持词(通用5篇)  主持词是主持人在台上表演的灵魂之所在。在现在的社会生活中,越来越多的...
悟空传的经典台词 悟空传的经典台词  1、我曾深爱过,我不在乎结局。  2、我知道天会愤怒,那,你知不知道,天也会颤抖...
最有创意的广告词(经典 最有创意的广告词(经典  01 钱不是问题,问题是没钱。  02 钻石恆久远,一颗就破產。  03 ...
毕业感谢致辞 关于毕业感谢致辞(精选15篇)  无论是在学校还是在社会中,大家都写过致辞吧,致辞的措词造句要考虑与...
年会嘉宾简短致辞 年会嘉宾简短致辞  在日复一日的学习、工作或生活中,大家总少不了要接触或使用致辞吧,致辞具有很强的实...
成长礼主持稿 成长礼主持稿(通用8篇)  在日常生活和工作中,需要使用主持稿的情况越来越多,主持稿是在晚会、联欢会...
电视剧《放羊的星星》经典台词 电视剧《放羊的星星》经典台词  在现实社会中,用到台词的地方越来越多,台词是一种特殊的,也是很难掌握...
抓周仪式主持词 抓周仪式主持词范文  主持词是主持人在台上表演的灵魂之所在。在如今这个中国,主持词是活动、集会等的必...
年终总结大会主持词结束语 年终总结大会主持词结束语  主持词是各种演出活动和集会中主持人串联节目的串联词。时代不断在进步,主持...
纯中式婚礼主持词(2) 让我们共同举起手中的酒杯,共同祝福我们这一对知心爱人,祝福他们在爱的旅途上风雨相承,相濡以沫,真爱一...
幼儿园园庆主持词 幼儿园园庆主持词  利用在中国拥有几千年文化的诗词能够有效提高主持词的感染力。在人们积极参与各种活动...
篮球比赛开幕式主持词 篮球比赛开幕式主持词(通用5篇)  主持词可以采用和历史文化有关的表述方法去写作以提升活动的文化内涵...
六一儿童节活动节目的主持词 六一儿童节活动节目的主持词(精选7篇)  主持词是各种演出活动和集会中主持人串联节目的串联词。在当今...
公司员工的感谢词 公司员工的感谢词3篇  我们虽然是公司的一名员工,其实也是公司的主人,需要有将公司当成家的态度,态度...
毕业晚会的主持稿 毕业晚会的主持稿(精选11篇)  在现在社会,我们很多时候都不得不用到主持稿,主持稿是主持人为节目进...
《加油金三顺》经典台词 《加油金三顺》经典台词  1、回忆是没有任何力量的。(三顺)  2、人都知道会死,但不还是活着吗?(...
升学酒会主持词 升学酒会主持词  借鉴诗词和散文诗是主持词的一种写作手法。在如今这个时代,司仪等是很多场合都需要的角...
秋季开学典礼颁奖主持词 秋季开学典礼颁奖主持词  活动对象的不同,主持词的写作风格也会大不一样。在人们积极参与各种活动的今天...
老人寿宴致辞 老人寿宴致辞(精选7篇)  在我们平凡的日常里,许多人都写过致辞吧,致辞具有“礼仪性”或“仪式化”的...