为了和C++标准库区分,以下代码除主函数外均在namespace空间
目录
首先一定要有个字符指针 _str,指向所存放的字符串。
还需要有字符串的大小_size,能够快速的知道这个字符串多大。
再需要有字符串的容量_capacity,知道字符串追加后是否需要扩容。
因此我们定义
- class string
- {
- private:
- char* _str;
- size_t _size;
- size_t _capacity;
- }
构造函数使用的是字符串来构造,并且给缺省值 "" ,注意里面没有空格,为了能够进行默认构造,并且这是一个字符串,虽然没有给到什么内容,但会存放一个'\0'。
_size和_capacity的大小就是str的长度。
_str需要使用new来开辟空间,开辟空间的大小是_capacity+1,因为之前strlen(str)算出来的长度都没有算'\0',因此你需要多开辟一个空间来存放'\0';
- string(const char* str = "")
- :_size(strlen(str))
- ,_capacity(_size)
- {
- _str = new char[_capacity + 1];
- strcpy(_str, str);
- }
为了实现深拷贝,就需要自己写拷贝构造和赋值运算符重载。
这里的拷贝构造函数和赋值我们选用了很简单的写法,使用带参构造函数构造出一个临时的类对象tmp,这个tmp对象的值正好是当前对象需要的,因此将他们两个交换一下,当前对象就完成了深拷贝了,同时tmp也会出了作用域也会自动调用析构函数进行析构(析构函数马上就写)。
赋值运算符重载也是利用的这种思想,甚至更加简单粗暴,不用为了节约而传 const string& s了,直接传string tmp,这样会调用一次拷贝构造,我要的就是拷贝出来的临时对象跟我互换,换了之后返回*this 即可
- void swap(string& s)
- {
- std::swap(_str, s._str);
- std::swap(_size, s._size);
- std::swap(_capacity, s._capacity);
- }
-
- string(const string& s)
- :_str(nullptr)
- ,_size(0)
- ,_capacity(0) //保证编译器一定初始化,析构才不会出错
- {
- string tmp(s._str);
- swap(tmp);
- }
- string& operator=(string tmp)
- {
- swap(tmp);
- return *this;
- }
析构函数非常简单,只需要将_str里面的内容delete掉即可,注意是需要delete[] ,顺便将_str置空, _size 和 _capacity赋值给0。
- ~string()
- {
- delete[] _str;
- _str = nullptr;
- _size = _capacity = 0;
- }
c_str()是将string类对象转化为字符串的格式,方便我们打印操作.
代码只需返回_str即可。
- const char* c_str() const
- {
- return this->_str;
- }

为了让string类对象像数组一样访问,我们需要重载 []
代码也很简单。这里写了两个版本,一个普通版一个const版,这为了让普通对象调用可以修改,const类对象的调用不能修改
- char& operator[](size_t pos)
- {
- assert(pos >= 0 && pos <= _size);
- return _str[pos];
- }
- const char& operator[](size_t pos) const
- {
- assert(pos >= 0&&pos<=_size);
- return _str[pos];
- }

让类外访问到private成员_size和_capacity的值。
- size_t size() const
- {
- return _size;
- }
- size_t capacity() const
- {
- return _capacity;
- }
reverse是扩容,如果n比_capacity小就不管,比_capacity打就按照n的大小来扩容。
流程为开辟空间,拷贝数据,释放空间,指向新空间,_capacity变为0
- void reverse(size_t n)
- {
- if (n > _capacity)
- {
- char* tmp = new char[n+1];//要多开一个留给'\0'
- strcpy(tmp, _str);
- if(_str!=nullptr)
- delete[] _str;
- _str = tmp;
- _capacity = n;
- }
- }
push_back是在尾部插入一个字符,插入之前判断空间是否足够,不足就扩容再插入,_size需要++,注意后面要置为'\0'。
append是在尾部插入一个字符串,依然要判断是否扩容,使用了库函数来进行拷贝,strcpy会拷贝'\0',因此无需关心'\0',将_size+=len即可
- void push_back(char ch)
- {
- if (_size == _capacity)
- {
- reverse(_capacity == 0 ? 4 : 2 * _capacity);
- }
- _str[_size] = ch;
- _size++;
- _str[_size] = '\0';
- }
- void append(const char* str)
- {
- size_t len = strlen(str);
- if (_size + len > _capacity)
- {
- reverse(_size + len);
- }
- strcpy(_str + _size, str);
- _size += len;
- }
使用+=比push_back和append爽的多,还不需要关心是字符还是字符串,因为我们运算符重载加函数重载了。
代码部分分别调用即可。
- string& operator+=(char ch)
- {
- push_back(ch);
- return *this;
- }
- string& operator+=(const char* str)
- {
- append(str);
- return *this;
- }

insert函数运用了重载,可以在你想要的位置插入字符和字符串。
他的主要思想是从后往前遍历,并且将数据往后面挪动,直到到了pos位置才会停止,然后将要插入的字符或者字符串放在pos位置即可。
只需要注意字符移动一格,字符串移动len格。
- void insert(size_t pos,char ch)
- {
- assert(pos <= _size && pos >= 0);
- if (_size == _capacity)
- {
- reverse(_capacity == 0 ? 4 : 2 * _capacity);
- }
- for(size_t i = _size + 1; i > pos; i--)
- {
- _str[i] = _str[i - 1];
- }
- _str[pos] = ch;
- _size++;
- }
- void insert(size_t pos, const char* str)
- {
- assert(pos <= _size && pos >= 0);
- size_t len = strlen(str);
- if (len+_size > _capacity)
- {
- reverse(len + _size);
- }
- for (size_t i = _size + len; i > pos; i--)
- {
- _str[i] = _str[i - len];
- }
- memcpy(_str + pos, str, len);
- _size += len;
- }
erase函数要分两种情况
一种是len的长度要大于等于_size-pos,也就是说要将pos后面的内容全部删除,这种我们处理起来很简单,将pos位置直接置为'\0',同时_size = pos即可。
另一种是后面还有字符需要保留,需要将后面的字符挪动到pos位置这里来,再_size -= len;
这里参数缺省值npos是静态成员函数,类型为size_t,值为-1,代表int的最大值
- void erase(size_t pos,size_t len = npos)
- {
- assert(pos < _size && pos >= 0);
- if (len >= _size - pos)
- {
- _size = pos;
- _str[_size] = '\0';
- }
- else
- {
- for (size_t i = pos + len; i <= _size; i++)
- {
- _str[i - len] = _str[i];
- }
- _size -= len;
- }
- }
clear函数简单,不需要处理_capacity,只需要第一个字符给到'\0',将_size给0。
- void clear()
- {
- _str[0] = '\0';
- _size = 0;
- }
resize也是两种情况
n小于等于_size时,证明_size要减小,直接在n这个位置给到'\0',_size给0就好
另一种情况就可能需要扩容了,我们直接暴力处理,不管你扩不扩容,先来个reverse(n),要扩容我就扩容,不需要我就返回继续执行后续代码,同时给后面的空间都赋值ch。不要忘记最后_size给0,还有最后给'\0'。
- void resize(size_t n,char ch = '\0')
- {
- if (n <= _size)
- {
- _str[n] = '\0';
- _size = n;
- }
- else
- {
- reverse(n);
- for (size_t i = _size; i < n; i++)
- {
- _str[i] = ch;
- }
- _size = n;
- _str[_size] = '\0';
- }
- }
find函数重载了,可以从某位位置往后找字符,或者字符串。
找字符很简单,一个循环完事。
找字符串用到了strstr()函数,不为空代表找到了,就返回找到的指针- _str指针,就能算出他们两个的差值,就是索引,指针为空就返回 npos。
- size_t find(char ch,size_t pos = 0)
- {
- for (size_t i = pos; i < _size; i++)
- {
- if (_str[i] == ch)
- {
- return i;
- }
- }
- return npos;
- }
- size_t find(const char* str, size_t pos = 0)
- {
- const char* p = strstr(_str + pos, str);
- if (p)
- {
- return p - _str;
- }
- return npos;
- }
substr是从pos位置,截取n个长度的字符串,返回类型为string
如果n == pos||n>=_size-pos即代表pos后面的长度都要截取到,算出pos后面还有多少个字符,给s开辟好空间,遍历直接直接+=即可。
另一种情况,就遍历到 pos + n,再进行+=
- string substr(size_t pos, size_t n = npos)
- {
- string s;
- if (n == npos || n >= _size - pos)
- {
- n = _size - pos;
- s.reverse(n);
- for (size_t i = pos; i < _size; i++)
- {
- s += _str[i];
- }
- }
- else
- {
- s.reverse(n);
- for (size_t i = pos; i < pos + n; i++)
- {
- s += _str[i];
- }
- }
- return s;
- }
迭代器是STL的特性,string类虽然比STL要早一点,还后面还是支持了迭代器,只不过因为该有的功能都有了,迭代器没有那么重要,但是可以为我们后续学其他容器打好基础。
string类的迭代器非常简单,就是一个指针,只需要写好begin() 和 end()就可以进行范围for的遍历了,指针本身就支持++和解引用,因此不需要重载。
我们写了两个迭代器,普通迭代器可以修改,const 迭代器不能修改
- 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;
- }
调用strcmp,再加复用
下面6种判断符重载,可以写在类里面,但string类为了支持宽字符等其他字符,写出了全局函数,这里只支持char类型,但还是写出了全局函数
- bool operator<(const string& s1, const string& s2)
- {
- return strcmp(s1.c_str(), s2.c_str()) < 0;
- }
- bool operator>(const string& s1, const string& s2)
- {
- return strcmp(s1.c_str(), s2.c_str()) > 0;
- }
- bool operator==(const string& s1, const string& s2)
- {
- return strcmp(s1.c_str(), s2.c_str()) == 0;
- }
- bool operator<=(const string& s1, const string& s2)
- {
- return !(s1>s2);
- }
- bool operator>=(const string& s1, const string& s2)
- {
- return !(s1
- }
- bool operator!=(const string& s1, const string& s2)
- {
- return !(s1 == s2);
- }
2.流输入和流输出运算符重载
流输出非常简单,直接遍历即可。
这里使用get()函数才可以获取到' '(空格)和'\n'(换行)
我们使用了临时数组来存储,目的是为了减少扩容,因为扩容的消耗很大。一个一个放到数组里面,等获取到' '(空格)和'\n'(换行) ,或者数据满了之后,再从数组里面将数据提取出来。
- ostream& operator<<(ostream& out, const string& s)
- {
- //out << s.c_str() << endl;
- for (auto e : s)
- {
- out << e;
- }
- return out;
- }
- istream& operator>>(istream& in,string& s)
- {
- s.clear();
- char tmp[129];
- size_t i = 0;
- char ch;
- ch = in.get();
- while (ch != ' ' && ch != '\n')
- {
- tmp[i++] = ch;
- if (i == 128)
- {
- tmp[i] = '\0';
- s += tmp;
- i = 0;
- }
- ch = in.get();
- }
- if (i != 0)
- {
- tmp[i] = '\0';
- s += tmp;
- }
- return in;
- }
到此,我们程序终于完成了,最后放上总代码
string.h
- #define _CRT_SECURE_NO_WARNINGS 1
- #pragma once
- #include
- #include
- using namespace std;
- namespace kky
- {
- class string
- {
- public:
- 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;
- }
- string(const char* str = "")
- :_size(strlen(str))
- ,_capacity(_size)
- {
- _str = new char[_capacity + 1];
- strcpy(_str, str);
- }
-
- //传统
- //string(const string& s)
- // :_size(s._size)
- // ,_capacity(s._capacity)
- //{
- // _str = new char[_capacity + 1];
- // strcpy(_str, s._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;
- //}
-
- void swap(string& s)
- {
- std::swap(_str, s._str);
- std::swap(_size, s._size);
- std::swap(_capacity, s._capacity);
- }
-
- string(const string& s)
- :_str(nullptr)
- ,_size(0)
- ,_capacity(0) //保证编译器一定初始化,析构才不会出错
- {
- string tmp(s._str);
- swap(tmp);
- }
- string& operator=(string tmp)
- {
- swap(tmp);
- return *this;
- }
-
- const char* c_str() const
- {
- return this->_str;
- }
- ~string()
- {
- delete[] _str;
- _str = nullptr;
- _size = _capacity = 0;
- }
- size_t size() const
- {
- return _size;
- }
- size_t capacity() const
- {
- return _capacity;
- }
- char& operator[](size_t pos)
- {
- assert(pos >= 0 && pos <= _size);
- return _str[pos];
- }
- const char& operator[](size_t pos) const
- {
- assert(pos >= 0&&pos<=_size);
- return _str[pos];
- }
-
- size_t find(char ch,size_t pos = 0)
- {
- for (size_t i = pos; i < _size; i++)
- {
- if (_str[i] == ch)
- {
- return i;
- }
- }
- return npos;
- }
- size_t find(const char* str, size_t pos = 0)
- {
- const char* p = strstr(_str + pos, str);
- if (p)
- {
- return p - _str;
- }
- return npos;
- }
-
- string substr(size_t pos, size_t n = npos)
- {
- string s;
- if (n == npos || n >= _size - pos)
- {
- n = _size - pos;
- s.reverse(n);
- for (size_t i = pos; i < _size; i++)
- {
- s += _str[i];
- }
- }
- else
- {
- s.reverse(n);
- for (size_t i = pos; i < pos + n; i++)
- {
- s += _str[i];
- }
- }
- return s;
- }
- void reverse(size_t n)
- {
- if (n > _capacity)
- {
- char* tmp = new char[n + 1];//要多开一个留给'\0'
- strcpy(tmp, _str);
- if (_str != nullptr)
- delete[] _str;
- _str = tmp;
- _capacity = n;
- }
- }
- void push_back(char ch)
- {
- if (_size == _capacity)
- {
- reverse(_capacity == 0 ? 4 : 2 * _capacity);
- }
- _str[_size] = ch;
- _size++;
- _str[_size] = '\0';
- }
- void append(const char* str)
- {
- size_t len = strlen(str);
- if (_size + len > _capacity)
- {
- reverse(_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;
- }
-
-
- void insert(size_t pos,char ch)
- {
- assert(pos <= _size && pos >= 0);
- if (_size == _capacity)
- {
- reverse(_capacity == 0 ? 4 : 2 * _capacity);
- }
- for(size_t i = _size + 1; i > pos; i--)
- {
- _str[i] = _str[i - 1];
- }
- _str[pos] = ch;
- _size++;
- }
- void insert(size_t pos, const char* str)
- {
- assert(pos <= _size && pos >= 0);
- size_t len = strlen(str);
- if (len+_size > _capacity)
- {
- reverse(len + _size);
- }
- for (size_t i = _size + len; i > pos; i--)
- {
- _str[i] = _str[i - len];
- }
- memcpy(_str + pos, str, len);
- _size += len;
- }
- void erase(size_t pos,size_t len = npos)
- {
- assert(pos < _size && pos >= 0);
- if (len >= _size - pos)
- {
- _size = pos;
- _str[_size] = '\0';
- }
- else
- {
- for (size_t i = pos + len; i <= _size; i++)
- {
- _str[i - len] = _str[i];
- }
- _size -= len;
- }
- }
- void clear()
- {
- _str[0] = '\0';
- _size = 0;
- }
- void resize(size_t n,char ch = '\0')
- {
- if (n <= _size)
- {
- _str[n] = '\0';
- _size = n;
- }
- else
- {
- reverse(n);
- for (size_t i = _size; i < n; i++)
- {
- _str[i] = ch;
- }
- _size = n;
- _str[_size] = '\0';
- }
- }
-
- private:
- char* _str;
- size_t _size;
- size_t _capacity;
- //const static size_t npos = -1; //特例
- const static size_t npos;
- };
-
- const size_t string::npos = -1;
-
- ostream& operator<<(ostream& out, const string& s)
- {
- //out << s.c_str() << endl;
- for (auto e : s)
- {
- out << e;
- }
- return out;
- }
- istream& operator>>(istream& in,string& s)
- {
- s.clear();
- char tmp[129];
- size_t i = 0;
- char ch;
- ch = in.get();
- while (ch != ' ' && ch != '\n')
- {
- tmp[i++] = ch;
- if (i == 128)
- {
- tmp[i] = '\0';
- s += tmp;
- i = 0;
- }
- ch = in.get();
- }
- if (i != 0)
- {
- tmp[i] = '\0';
- s += tmp;
- }
- return in;
- }
-
- bool operator<(const string& s1, const string& s2)
- {
- return strcmp(s1.c_str(), s2.c_str()) < 0;
- }
- bool operator>(const string& s1, const string& s2)
- {
- return strcmp(s1.c_str(), s2.c_str()) > 0;
- }
- bool operator==(const string& s1, const string& s2)
- {
- return strcmp(s1.c_str(), s2.c_str()) == 0;
- }
- bool operator<=(const string& s1, const string& s2)
- {
- return !(s1>s2);
- }
- bool operator>=(const string& s1, const string& s2)
- {
- return !(s1
- }
- bool operator!=(const string& s1, const string& s2)
- {
- return !(s1 == s2);
- }
-
- void test01()
- {
- string s("123456");
- cout << s.c_str() << endl;
- s[0] = 'c';
- cout << s.c_str() << endl;
- /*for (int i = 0; i < s.size(); i++)
- {
- cout << s[i] << " ";
- }
- cout << endl;
- string::iterator it = s.begin();
- while (it != s.end())
- {
- cout << *it << " ";
- it++;
- }
- cout << endl;
- for (auto e : s)
- {
- cout << e << " ";
- }*/
- }
- void test02()
- {
- string s1("hello world");
- cout << s1.c_str() << endl;
- s1.push_back('s');
- s1.append("tring");
- cout << s1.c_str() << endl;
- s1 += 'w';
- s1 += "odema";
- cout << s1.c_str() << endl;
- }
-
- void test03()
- {
- string s1("hello world");
- cout << s1.c_str() << endl;
- s1.insert(5, 'c');
- cout << s1.c_str() << endl;
- s1.insert(s1.size(), 'c');
- cout << s1.c_str() << endl;
- s1.insert(0, 'c');
- cout << s1.c_str() << endl;
- s1.insert(5, "hhh");
- cout << s1.c_str() << endl;
- s1.erase(0, 3);
- cout << s1.c_str() << endl;
- s1.erase(s1.size()-1, 3);
- cout << s1.c_str() << endl;
- }
- void test04()
- {
- string s1("hello world");
- cout << s1.c_str() << endl;
- string s2("hello worldl");
- cout << (s1 != s2) << endl;;
- cout << s2 << endl;
- cin >> s1;
- cout << s1.c_str() << endl;
- }
- void test05()
- {
- string s1("hello world");
- cout << s1.c_str() << endl;
- string s2("hello worldl");
- cout << s2 << endl;
- s1.resize(5);
- cout << s1 << endl;
- s1.resize(15,'c');
- cout << s1 << endl;
- }
- void test06()
- {
- string s1("hello world");
- string s2(s1);
- cout << s1 << endl;
- cout << s2 << endl;
- string s3 = "wasg1";
- s3 = s1;
- cout << s3 << endl;
- }
- }
test.cpp (调用测试接口即可)
- #include"string.h"
- int main()
- {
- kky::test06();
-
- }
-
相关阅读:
1.创建Django项目
koa框架(二) mvc 模式及实现一个koa框架的web服务
Typora设置代码块Mac风格三个圆点
通过生成指标功能从非指标数据中分析趋势
2022年,软件测试还能学吗?别学了,软件测试岗位饱和了...
十大运动蓝牙耳机品牌排行榜,六款值得买的运动耳机推荐
高并发编程:并发容器
MFC ExtTextOut函数学习
Excel的VLOOKUP函数的用法
运营版uniapp多商户商城小程序+H5+APP+商家入驻短视频社区种草直播阶梯拼团
-
原文地址:https://blog.csdn.net/kkbca/article/details/133886604