郑州做的比较好网站公司,电子商务网站的运营一般需要做哪些准备,东莞最新消息今天,如何建设一家网站目录
前言#xff1a;
1. string框架构造
2. 默认函数
2.1 构造函数
2.2 析构函数
2.3 拷贝构造
2.4 赋值重载
3. 迭代器 4. 整体程序 前言#xff1a; 本篇文章模拟实现了C中string的部分功能#xff0c;有助于大家了解和熟悉string类#xff0c;虽然这个类不难实…目录
前言
1. string框架构造
2. 默认函数
2.1 构造函数
2.2 析构函数
2.3 拷贝构造
2.4 赋值重载
3. 迭代器 4. 整体程序 前言 本篇文章模拟实现了C中string的部分功能有助于大家了解和熟悉string类虽然这个类不难实现但是其中有些小细节还是可以细细品味的。
1. string框架构造 我们得知道编写一个类的首要步骤不是直接开写代码而是需要思考这个类需要用什么样的数据结构需要准备哪些参数这个类的作用域在哪里等等内容。 首先咱们知道string类是有多个版本的不同版本下的string类的变量是不同的就我所知在vs下string的实现是下方的格式实际上比我写的要复杂但是能够表示。 class string{ private: size_t _size; size_t _capacity; char* _buf; char _arr[16]; }; 这样做的用处主要是空间换取时间当我们只是一个小字符串那么就直接将数据存到了_arr的数组当中因为是直接写在类里面的数组所以就省去了向堆申请空间这一过程如果数据大于了_arr数组大小那么系统才会向堆申请空间。所以在vs下的string类有28个字节。 在Linux下则不同string类有8个字节结构如下至于为什么是8个字节那是因为Linux下跑C代码默认是64位机哦我现在还不会改为32位运行请原谅博主。 class string{ private: m_string* _point; } 其中m_string是一个自定义类型里面存了和vs下差不多样式的变量只不过没有数组的存在如下 class m_string{ size_t _size; size_t _capacity; char* _buf; }; 这样做是因为Linux有一个特性那就是赌它赌我们在拷贝时不需要更改不改那么他就直接将指针指向这个空间一旦需要更改那他就会触发写时拷贝这个牛技术。如下图 对此我只能是表示这些大佬们写代码是真牛哇为了提升代码效率都能卷成这样了我们自己的实现就不用这么高级的操作了太难写了。既然是为了理解string类所以我决定用下方的结构简单清楚好写 class string{ private: char* _buf; size_t _size; size_t _capacity; }; 还有一点我们的这个类尽量写在一个自己的命名空间当中防止与库中的string起冲突。
如下
namespace yf
{class string{ private:char* _buf;size_t _size;size_t _capacity;};
}
2. 默认函数
2.1 构造函数 先看一下C库中提供的构造函数 真是有够恐怖的这提供的接口有点猛不过我们呢也不需要写这么多写几个常用的就好。还有就是这些接口设计得有一些不合理比如说default和from c-string两个函数很明显是可以写为一个函数的以缺省参数来表示的不过也能理解毕竟C是为了兼容C的。
代码
//构造函数string(const char* str ):_size(strlen(str))
{_capacity _size; _buf new char[_capacity 1];strcpy(_buf, str);
} 上方我讲解了可以用缺省参数来合并两个函数接口但是大家再不看我的代码之前能想出来吗除此之外我还想了两个方法一个是 ‘ ’另一个是“\0”有人看出不对了吗这两个方式都是不行的第二种倒也不是不行而是太多余了本身“”里面就已经有了一个‘\0’了显得我们不够专业那这可不行。第一种用字符方式缺省那是真的错得离谱我们之后可是要用字符串呢用字符接收这很明显不行哇所以这种情况是一定要避免的。不然以后去公司面试让我们写个类都乱写简历上还写着熟悉C这不扯淡嘛。还有一点为了保持new空间和delete保持一致 然后我们的构造函数当中还用了初始化列表这一概念通过我们传入的字符串长度去初始化_size然后_capacity又在下方被赋值再为_buf向堆上申请_capacity1个字节大小的空间为什么加1当然是为了存我们的\0哇小笨蛋最后再使用strcpy函数将字符拷贝到我们的空间当中。 测试用例
void test1()
{yf::string str1;yf::string str2(hello);yf::string str3(good morning xxxxxxxxxxxxx);
}
2.2 析构函数 大伙们回忆一下为什么我们要有析构函数因为我们向堆空间申请了空间了所以需要释放避免造成内存泄漏的过程。
//析构
~string()
{delete[] _buf;_size 0;_capacity 0;
} 看着这里释放内存的方式是delete[] _buf也就与我们的构造函数对应起来了如果构造函数的缺省参数是‘’的话这里就不好析构就得分情况这不是脱了裤子放屁——多此一举嘛。
2.3 拷贝构造 拷贝构造和构造差不多我也就不多做解释了只是要注意参数得用引用。
string(const string str)
{_capacity str._capacity;_size str._size;_buf new char[str._capacity 1];strcpy(_buf, str._buf);
}
2.4 赋值重载 如果是没有数据的重载好简单但是呢我的这个字符串本来是有数据的另外被拷贝对象有大于的情况有小于的情况有等于的情况如果拷贝错误怎么办原来的数据这么办这一些列的问题问下来还简单嘛依然简单只不过多数人在一时间想不到完全实现功能的代码罢了。
//赋值重载
string operator(const string str)
{//不能自己拷贝自己if (this ! str){//当前容量大于被拷贝对象并不多时if (_capacity - str._capacity 10){strcpy(_buf, str._buf);_capacity str._capacity;_size str._size;}else{//考虑到new失败的情况char* temp new char[str._capacity 1];strcpy(temp, str._buf);delete[] _buf;_capacity str._capacity;_size str._size;}}return *this;
} 请看我的代码首先我们不能直接赋值给自己这样会让我们把数据给delete掉了导致整个程序出BUG了还不知道这是很危险的。然后呢我还考虑到了当当前对象的容量大于被拷贝对象并且大于的值并不多时可以直接拷贝不用delete后再整个赋值节省了时间。 在下方需要delete的过程中我们需要考虑到当new失败之后会怎么样所以不能先释放掉原空间需要创建一个变量去接收被拷贝对象的数据如果new失败了外部的try会直接接收到。
3. 迭代器 说起string类等一系列库类里面都是提供了迭代器的我们也模仿着在我们的类里面实现迭代器我们呢还是从简用指针来实现迭代器不过我们不能将迭代器理解为指针只是指针能够实现。
定义 typedef char* iterator; typedef const char* const_iterator; 对应接口
//迭代器begin
iterator begin()
{return _buf;
}
const_iterator begin() const
{return _buf;
}//迭代器end
iterator end()
{return _buf _size;
}const_iterator end() const
{return _buf _size;
} 此时我们的string类就能实现范围for这个语法糖了至于为什么能实现我只能说范围for底层就是迭代器。
测试
void test2()
{const yf::string str1(hello world);yf::string::const_iterator it str1.begin();while (it ! str1.end()){cout *it ;it;}cout endl;for (auto e : str1){cout e ;}cout endl;
} 4. 整体程序 其余的函数都是对于string类的补充比如reserve、resize、、[]、、函数等等博主很懒并且认为这些大家都是有基础能实现的所以就不做讲解了附下代码
#pragma once#includeiostream
#includestring
#includeassert.h
using std::cout;
using std::cin;
using std::endl;namespace yf
{class string{friend std::ostream operator(std::ostream out, const string str);friend std::istream operator(std::istream in, yf::string str);public:typedef char* iterator;typedef const char* const_iterator;//构造函数string(const char* str ):_size(strlen(str)){_capacity _size; _buf new char[_capacity 1];strcpy(_buf, str);}//拷贝构造string(const string str){_capacity str._capacity;_size str._size;_buf new char[str._capacity 1];strcpy(_buf, str._buf);}//赋值重载string operator(const string str){//不能自己拷贝自己if (this ! str){//当前容量大于被拷贝对象并不多时if (_capacity - str._capacity 10){strcpy(_buf, str._buf);_capacity str._capacity;_size str._size;}else{//考虑到new失败的情况char* temp new char[str._capacity 1];strcpy(temp, str._buf);delete[] _buf;_capacity str._capacity;_size str._size;}}return *this;}char operator[](size_t pos){assert(pos _size);return _buf[pos];}const char operator[](size_t pos) const{assert(pos _size);return _buf[pos];}//tidy_capacityvoid reserve(size_t n);void resize(size_t n, char c \0);//addvoid push_back(char c);void append(const string str);void append(const char* s);string operator(char c){push_back(c);return *this;}string operator(const string str){append(str);return *this;}string operator(const char* s){append(s);return *this;}//insertstring insert(size_t pos, size_t n, char c);string insert(size_t pos, const char* s);string insert(size_t pos, const string str);//迭代器beginiterator begin(){return _buf;}const_iterator begin() const{return _buf;}//迭代器enditerator end(){return _buf _size;}const_iterator end() const{return _buf _size;}//输出void print() const{cout _buf endl;}const char* c_str() const{return _buf;}//大小size_t size() const {return _size;}//容量size_t capacity() const{return _capacity;}//匹配字符size_t find_first_of(char c, size_t pos 0){const_iterator it begin();it pos;while (it ! end()){if (*it c){return (it - begin());}it;}return -1;}//获取字串string substr(size_t pos 0, size_t len -1) const;//析构~string(){delete[] _buf;_size 0;_capacity 0;}const size_t npos -1;private:char* _buf;size_t _size;size_t _capacity;};
}
#define _CRT_SECURE_NO_WARNINGS 1#includestring.h//tidy_capacity-reserve
void yf::string::reserve(size_t n)
{if (n _capacity){char* temp new char[n 1];strcpy(temp, _buf);delete[] _buf;_buf temp;_capacity n;}
}//tidy_capacity-resize
void yf::string::resize(size_t n, char c)
{//长度小于_size时需要删除n位置后的数据if (n _size){_size n;_buf[_size] \0;}else{//当n_size有两种情况大于capacity和小于capacityif (n _capacity){//大于需要扩容操作reserve(n); //复用reserve}//二者都有同样的需求对后面的数据初始化为cwhile (_size ! n){_buf[_size] c;_size;}_buf[_size] \0;}
}//add
void yf::string::push_back(char c)
{if (_size 1 _capacity){//加一预防_capacity初始为0reserve(2 * _capacity 1);}_buf[_size] c;_buf[_size] \0;
}void yf::string::append(const string str)
{if (_size str._size _capacity){reserve(_size str._size);}size_t len str._size;int i 0;while (i!len){_buf[_size] str[i];i;}_buf[_size] \0;
}void yf::string::append(const char* s)
{size_t len strlen(s);if (_size len _capacity){reserve(_size len);}while (*s ! \0){_buf[_size] *s;s;}_buf[_size] \0;
}//insert
yf::string yf::string::insert(size_t pos, size_t n, char c)
{if (_size n _capacity){reserve(_size n);}size_t end _size n;while (end - n 1 pos){_buf[end] _buf[end - n];--end;}for (size_t i 0; i n;i){_buf[pos i] c;}_size n;return *this;
}//插入字符串
yf::string yf::string::insert(size_t pos, const char* s)
{size_t len strlen(s);if (_size len _capacity){reserve(_size len);}size_t end _size len;while (end - len 1 pos){_buf[end] _buf[end - len];--end;}strncpy(_buf pos, s, len);_size len;return *this;
}yf::string yf::string::insert(size_t pos, const yf::string str)
{size_t len str._size;if (_size len _capacity){reserve(_size len);}size_t end _size len;while (end - len 1 pos){_buf[end] _buf[end - len];--end;}strncpy(_buf pos, str._buf, len);_size len;return *this;
}//获取子串
yf::string yf::string::substr(size_t pos, size_t len) const
{yf::string::const_iterator it begin();yf::string temp;it pos;if (len size())len size();while (it ! begin() len){temp *it;it;}return temp;
}//流插入
std::ostream yf::operator(std::ostream out, const yf::string s)
{for (auto ch : s){out ch;}return out;
}//流提取
std::istream yf::operator(std::istream in, yf::string s)
{//从缓冲区获取字符char ch in.get();while (ch ! \n){s ch;//记录上一次位置拿到下一个字符ch in.get();}return in;
}