多多进宝怎么做自己网站,影视制作公司简介,系统优化的目的和意义,千阳县住房和城乡建设局网站欢迎来到博主的专栏——c编程 博主ID#xff1a;代码小豪 文章目录 前言string类的模拟实现string的成员对象构造、赋值、析构访问成员对象的接口访问字符串中的元素迭代器对字符序列的插入、删除元素操作mystring类的相关操作 mystring类的所有模拟实现以及测试案例 前言
本…欢迎来到博主的专栏——c编程 博主ID代码小豪 文章目录 前言string类的模拟实现string的成员对象构造、赋值、析构访问成员对象的接口访问字符串中的元素迭代器对字符序列的插入、删除元素操作mystring类的相关操作 mystring类的所有模拟实现以及测试案例 前言
本片的string类的模拟实现不涉及模板泛型编程并不是本专栏的重点内容本专栏的主要目的是了解c面向对象编程的特性以及STL的部分使用方法。因此本博客模拟的string类是为了让读者了解类的封装方法、接口设计。
string类的模拟实现
string的成员对象
一个类需要设计该类的属性和行为。属性是成员对象而行为则是成员函数string类是字符序列的类。
c是基于C语言扩展而成因此c当中的字符序列和c语言的如出一撤即字符序列的字符在内存中连续存储再用一个char*的指针指向字符序列。c称这种字符序列为c-string。
如果要将字符串封装起来那么我们还需要提供其他的属性来显示这个字符序列的状态比如当前字符串的长度。
我们还想要这个字符串可以自动的扩大存储。那么最好的方法就是使用动态内存来管理这个字符序列。因此还需要为其设计容量这一属性。方便用户即类的使用者查看当前字符序列的可存储内存。
那么根据上述理论我们可以确定一个基本的string类应该封装这么三个对象c-string大小可存储容量。
class mystring
{
private:char* _str;//c-stringsize_t _size;//当前的字符长度size_t _capacity;//容量
};c的string类还存在一个特殊的static const成员常量npos我们也将其设计在类中但是要注意static成员变量要声明在类中定义在类外。
cpp
class mystring
{
private:char* _str;//c-stringsize_t _size;//当前的字符长度size_t _capacity;//容量static const size_t npos;
};
const size_t mystring::npos -1;构造、赋值、析构
一个类想要正常的使用那么为其设计合理的构造、析构、赋值成员函数时必不可少的即使你粗心的遗忘了某个部分编译器都会为你生成这些函数因此想要设计好一个类这个模块是必不可少的。
我们希望string类的构造能支持下面三种初始化形式默认初始化成空string也可以用c-string初始化这个string还可以用string对象初始化string。因此我们需要设计这么三个函数。默认构造、拷贝构造、和c-string作为参数的构造。
class mystring
{
public:mystring();//默认构造mystring(const mystring str);//拷贝构造mystring(const char* str);//c-string构造
private:char* _str;//c-stringsize_t _size;//当前的字符长度size_t _capacity;//容量
};我们最好将声明和定义分离在两个编程单元当中这是为了减少链接问题。由于篇幅问题博主就不详细声明了博主将会在c杂谈中提到这个。
string的默认构造默认构造是构造一个空字符串。空字符串长度为0但是内存并不为0因为空字符串并不是没有字符而是开辟了一个只有‘\0’的字符串。
mystring::mystring()
{_size 0;_capacity 0;_str new char[_capacity 1] {0};
}为什么申请的空间要比_capacity多一个呢这是因为容量不需要注意\0的管理我们设计的容量是为了记录可存储有效字符的容量但是在申请空间的时候需要比容量多申请一个这个位置是留给\0的。
拷贝构造还是为了拷贝c-string的。因此不仅仅string对象中的_str指向的字符序列与c-string一致而且_size和_capacity都要与c-string一致。
mystring::mystring(const char* str):_size(strlen(str))
{_capacity _size;_str new char[_capacity 1];strcpy(_str, str);
}博主这里使用了C语言的库函数strlen和strcpyC语言的string.h在string类的模拟实现中大量使用主要原因还是博主懒如果大家想了解strlen和strcpy的使用逻辑那么可以去看博主在C语言进阶指南14里面有相对应的模拟实现。模拟实现是为了让自己能够对最近的知识进行一次实践、而不是造轮子博主当前的阶段还能写string类能比设计标准库大佬好这当然不可能。 拷贝构造函数是将待拷贝的mystring对象的所有属性都拷贝过来。
mystring::mystring(const mystring str)
{_size str._size;_capacity str._capacity;//这里是深拷贝_str new char[_capacity 1];strcpy(_str, str._str);
}析构函数则是将申请的空间进行释放由于字符序列不再存在有效字符因此_size和_capacity置为0。
mystring::~mystring()
{delete[] _str;_size 0;_capacity 0;
}赋值重载函数的本质也是将对象参数进行拷贝与拷贝构造的原理相同。但是要将申请的空间进行销毁。
mystring mystring::operator(const mystring str)
{_size str._size;_capacity str._capacity;delete[] _str;_str new char[_capacity 1];strcpy(_str, str._str);return *this;
}访问成员对象的接口
mystring中的成员对象都被隐藏了起来如果我们想要让用户知道这些数据可以为其设计访问的接口方便用户进行操作而不破坏类的封装性。 size_t size() { return _size; }size_t capacity() { return _capacity; }void reserve(size_t n);//扩容函数void clear();//清空clearreserve的目的是让用户可以手动的为mystring对象进行扩容。当然我们也可以在成员函数当中调用这个函数完成扩容可谓是一举两得。
由于c并没有给出像realloc这种可以原地扩容的关键字因此博主在reserve当中使用的是异地扩容。
void mystring::reserve(size_t n)
{if (n _capacity){size_t newcapacity _capacity 0 ? 4 : 1.5 * _capacity;//异地扩容char* tmp new char[newcapacity 1] {0};strcpy(tmp, _str);delete[] _str;_str tmp;_capacity newcapacity;}
}clear是将字符串里的有效字符清空我们不需要修改容量只需要修改大小并且将字符序列的起始字符换成‘\0’就行了。因为_str的本质是c-string而c-string的定义就是从起始字符开始到第一个遇到的’\0’为c-string。
void mystring::clear()
{_str[0] \0;_size 0;
}访问字符串中的元素
mystring对象时一个字符序列因此我们需要考虑用户该如何访问字符序列的元素。由于_str是一个c-string那么我们可以考虑C语言的做法用下标访问符[]访问元素。这就需要我们为mystring重载一个下标访问符的函数了。 char operator[](size_t pos) { return _str[pos]; }const char operator[](size_t pos)const { return _str[pos]; }由于我们需要考虑到const对象和non-const对象调用此函数的不同效果因此需要将其重载一个const对象的调用版本和一个非const对象的调用版本。
迭代器
迭代器是一个用来在容器、对象当中遍历或者访问元素的接口。由于c-string可以用char的指针来遍历或访问元素。因此我们不妨将char的指针作为mystring的迭代器。 typedef char* iterator;typedef const char* const_iterator;iterator begin() { return _str; }//返回对象的起始迭代器iterator end() { return _str _size; }//返回对象的末尾迭代器const_iterator end() const{ return _str _size; }//返回const对象的末尾迭代器const_iterator begin() const{ return _str _size; }//返回const对象的起始迭代器对字符序列的插入、删除元素操作
我们设计为字符序列的相关修改函数字符序列本身是一个顺序表的数据结构因此我们设计插入、删除元素的函数时可以参考顺序表的插入、删除元素的算法。 void push_back(char c);//追加字符void append(const mystring str);//追加字符串mystring operator(const mystring str);//追加字符串mystring operator(char c);//追加字符void insert(size_t pos, char ch);//在pos的位置插入字符void insert(size_t pos, const char*str);//在pos的位置插入字符void erase(size_t pos 0, size_t len npos);//删除pos位置后len个长度的字符void swap(mystring s);//交换字符串追加字符或字符串的操作可以参考顺序表的尾插法。尾插是在字符串的末尾加入元素。 追加的过程中要注意mystring对象的容量可能满了注意为该对象进行扩容。 void mystring::push_back(char c){if (_size _capacity)//扩容{size_t newcapacity _capacity 0 ? 4 : 1.5 * _capacity;reserve(newcapacity);_capacity newcapacity;}_str[_size] c;_str[_size] \0;}void mystring::append(const mystring str){size_t len strlen(str._str);while (_size len _capacity)//扩容{size_t newcapacity _capacity 0 ? 4 : 1.5 * _capacity;reserve(newcapacity);_capacity newcapacity;}size_t end _size len;size_t i 0;strcpy(_str,str._str);}mystring mystring::operator(const mystring str){append(str);//这里我们直接复用追加函数return *this;}mystring mystring::operator(char c){push_back(c);//复用追加函数return *this;
插入操作也是和顺序表的插入算法一致因为字符序列的本质就是一个顺序表。
void mystring::insert(size_t pos, char ch){assert(_size pos);//检测合法性if (_size _capacity)//扩容{size_t newcapacity _capacity 0 ? 4 : 1.5 * _capacity;reserve(newcapacity);_capacity newcapacity;}size_t end _size;while (end pos)//挪动数据{_str[end] _str[end - 1];end--;}_str[pos] ch;//插入数据_size;}void mystring::insert(size_t pos, const char* str){assert(_size pos);//判断合法性size_t len strlen(str);//判断插入字符的有效字符个数size_t newsize _size len;while (newsize _capacity)//扩容{size_t newcapacity _capacity 0 ? 4 : 1.5 * _capacity;reserve(newcapacity);_capacity newcapacity;}size_t end newsize;while (end poslen)//挪动数据{_str[end] _str[end - len];end--;}while (len--)//插入数据不会插入‘\0’{_str[pos len] str[len];}_size newsize;}删除数据则是将该范围的数据被后面的数据覆盖就行
void mystring::erase(size_t pos, size_t len ){assert(pos _size);if(_size -pos len){_str[pos] \0;_size pos;}else{size_t end _size;size_t begin pos len;while (begin end){_str[begin - len] _str[begin];begin;}_size - len;}}mystring类的相关操作
我们希望mystring类可以用于C语言的函数换句话说就是让mystring中的_str拿出来是用于C语言设计的函数。 const char* c_str() const{ return _str; }我们还可以设计一个查找函数方便我们查找字符或字符串在mystring对象当中的位置。 size_t mystring::find(char ch, size_t pos )const{assert(pos _size);while (_str[pos] ! \0){if (_str[pos] ch)//查找字符{return pos;}pos;}return npos;//返回字符}size_t mystring::find(const char* str, size_t pos )const{assert(pos _size);const char* substr;substr strstr(_strpos, str);//查找字符串if (substr nullptr){return npos;}return substr-_str;//返回字符串}查找字符串会用到复杂的算法比如KMR查找算法这里博主不多讲述所以用strstr这个C语言函数并且利用指针相减的特性取巧的解决了这个问题。
我们还可以重载io流使得cout和cin可以对mystring类的对象进行操作注意这两函数是定义成非成员函数的。 istream operator (istream is, mystring str);ostream operator (ostream os, const mystring str);istream operator (istream is, mystring str){str.clear();char ch0;chis.get();while (ch ! \n ch ! ){str ch;ch is.get();}return is;}ostream operator (ostream os, const mystring str){os str.c_str();return os;}mystring类的所有模拟实现以及测试案例
博主将mystring类的模拟实现的所有代码以及测试案例都放在博主本人的代码仓库当中。欢迎查阅。 博主的gitee代码小豪的代码仓库