当前位置: 首页 > news >正文

莱芜市莱城区城乡建设局网站o2o服务平台

莱芜市莱城区城乡建设局网站,o2o服务平台,seo刷词工具在线,wordpress 发布说说目录 前言 一、接口总览 二、默认成员函数 1.构造函数 2.拷贝构造 写法一#xff1a;传统写法 写法二#xff1a;现代写法#xff08;复用构造函数#xff09; 3.赋值构造 写法一#xff1a;传统写法 写法二#xff1a;现代写法(复用拷贝构造) 4.析构函数 三、…目录 前言 一、接口总览 二、默认成员函数 1.构造函数 2.拷贝构造 写法一传统写法 写法二现代写法复用构造函数 3.赋值构造 写法一传统写法 写法二现代写法(复用拷贝构造) 4.析构函数 三、迭代器 begin和end函数 四、遍历与访问 operator[]函数 五、容量和大小 size capacity empty resever容量 resize大小 六、修改 insert erase push_back append operator clear清理字符串内容 c_str swap 七、查找 find substr左闭右开区间 八、非成员函数重载 swap全局string 关系运算符重载 重载 重载 getline函数 前言 在前面我们已经了解了string中常见的接口我们也说过对于STL库的学习不仅仅只是熟用还要明理但是要注意一个道理模拟实现不是为了比库里面更好而是去学习它的一些底层能够让自己有更深的了解就比如我们并不需要去造车这不是我们干的但我们需要了解它为什么能这样做它的底层是什么在驱动这样才能更好的去发挥车的性能string类的底层就是个字符顺序表 一、接口总览 #includeassert.h #includeiostreamnamespace STR {class string{public:typedef char* iterator;typedef const char* const_iterator;static const size_t npos;public:string(const char* str );//构造函数提供全缺省可以是空参数调用也可以有参调//s2(s1)string(const string s);//拷贝构造,传统写法string(const string s);//拷贝构造现代写法//s1s2string operator(const string s);//赋值重载传统写法string operator(string tmp);//赋值重载,现代写法~string();//析构函数//迭代器iterator begin();iterator end();const iterator begin()const;const iterator end()const;// 遍历char operator[](size_t index);const char operator[](size_t index)const;// 容量size_t size()const;size_t capacity()const;bool empty()const;void reserve(size_t n);void resize(size_t n, char c \0);// 修改void push_back(char c)string operator(char c);void append(const char* str);string operator(const char* str);void clear();void swap(string s);const char* c_str()const;// 返回c在string中第一次出现的位置size_t find(char c, size_t pos 0) const;// 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos 0) const;// 在pos位置上插入字符c/字符串str并返回该字符的位置string insert(size_t pos, char c);string insert(size_t pos, const char* str);// 删除pos位置开始向后len个元素并返回该元素的下一个位置string erase(size_t pos, size_t lennpos);string sub(size_t pos 0, size_t len npos); private:char* _str;size_t _capacity;size_t _size;};const size_t string::npos -1;//relational operators//s1s2bool operator(const string s1, const string s2);bool operator(const strings1,const string s2);bool operator(const string s1, const string s2);bool operator(const string s1, const string s2);bool operator(const string s1, const string s2);bool operator!(const string s1, const string s2);ostream operator(ostream _out, const string s);istream operator(istream _cin, string s);istream getline(istream in, string s);} 注意这里模拟实现需要我们使用我们自己的命名空间防止与库里面的发生冲突 二、默认成员函数 1.构造函数 构造函数一般提供缺省参数这样使得无参有参也可以一起调用 string(const char* str )//构造函数提供全缺省可以是空参数调用也可以有参调用,但是单参数构造会有隐式类型转化:_size(strlen(str))//初始时大小设置为字符串长度{_capacity _size;//容量设置为字符串长度_str new char[_capacity 1];//多开一个是为存放\0strcpy(_str, str);//按字节拷贝} 2.拷贝构造 对于拷贝构造前面我们也说过一定要注意深拷贝和浅拷贝的区别以及影响这里再回顾一下 浅拷贝不写编译器默认生成的函数多个对象共同指向同一块资源那么释放时会造成同一块空间多次释放的问题 深拷贝我们自己写的给每个对象分配独立的资源保证多个对象之间不会因为共享资源而造成多次释放问题 写法一传统写法 思路简单先开一个与原对象大小相同的空间这样两个对象指向的空间就不一样了相互独立然后把字符串按字节拷贝过去再把相应的值传给拷贝对象 string(const string s)//拷贝构造 {_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._capacity;} 写法二现代写法复用构造函数 思路与传统的不同它需要借助一个tmp对象先将原对象的字符串通过调用构造函数创建出tmp对象最后在将tmp和拷贝对象交换这样两个对象指向的空间也是不一样的 string(const string s):_str(nullptr)//拷贝对象一定要先置空,_size(0),_capacity(0){string tmp(s._str);//调用构造函数不是拷贝构造因为参数不是对象是个字符串swap(tmp);//交换拷贝对象和tmp//tmp出了作用域会自动销毁,此时它指向的空间是个空析构时不会出问题如果是随机值就会出现问题//这就为什么_str先前要置空的原因} 需要注意上面的细节问题 两种写法效率上都是一样因为都要开空间只不过传统写法是要自己写而现代写法直接复用构造函数喜欢写哪个就写哪个没啥区别 3.赋值构造 这里也是有深浅拷贝的问题但是我们时刻都不能让多个对象指向同一块空间所以还是要自己写深拷贝 写法一传统写法 注意我们不能直接将原对象拷贝过去因为两者空间容量大小可能不一样所以要新开一个空间在让赋值对象指向这块空间即可 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;}} 写法二现代写法(复用拷贝构造) string operator(string tmp)//赋值重载 {swap(tmp);//交换两个对象return *this;//返回左值支持连续赋值 } 可以看到这里的参数我们利用值传递接受右值的方法编译器会自动调用拷贝构造函数去生成一个tmp对象出来最后在与左值进行交换即可这样两者指向的空间就是不一样的那么这里或许会有疑问传值不会死循环吗这里当然不会因为赋值它只会调用一次拷贝构造但是拷贝构造是一定不能传值的因为传值会再一次调用拷贝构造一次又一次最终形成死循环 4.析构函数 对于string类我们需要自己手动写析构因为每个对象中的_str指向的是一块堆空间不写编译器自动生成的析构他只是会对内容进行清理而不会自己释放空间所以为了避免内存泄漏我们需要手动delete ~string()//析构函数 {delete[] _str;_str nullptr;_capacity _size0; }三、迭代器 string类的迭代器底层就是个字符指针只不过是把它重命名罢了 typedef char* iterator;typedef const char* const_iterator; 但是需要注意的是并不是所有的迭代器都是指针只能说像不一定是上面我说的就是简单版本的 begin和end函数 这两个函数简单得可怕begin实际上是返回第一个字符的地址end就是返回最后一个字符的下一个位置的地址 //begin iterator begin() {return _str; }iterator begin()const //让const对象也能用 {return _str; } //end iterator end() {return _str _size; }iterator end()const {return _str _size; } 这里提一嘴在上一篇文章string类中提到过范围for它并神奇它的底层就是迭代器实际上这个东西在底层是写死的虽然是迭代器但是如果我们在模拟实现时把begin函数改成Begin函数那么范围for会立马失效也就是说它只认识begin和end而不认识Begin和End死的大家可以尝试尝试 四、遍历与访问 operator[]函数 我们之前能够通过【】下标进行访问字符串其实底层就是这个函数这个函数就是参数就是一个位置直接返回对应字符的引用即可 //这个是可读可写的 char operator[](size_t index) {assert(index _size);//对下标是否越界的判断return _str[index];//返回对应位置即可 }//这个是只读的访问const对象用的 char operator[](size_t index)const {assert(index _size);return _str[index]; } 五、容量和大小 size //直接返回对象的变量_size就好了 //对于一些只需要进行读操作的我们一般习惯加上const修饰 size_t size()const {return _size; } capacity size_t capacity()const {return _capacity; } empty bool empty()const {if (_size 0){return true;}return false; } resever容量 规则如果n大于当前的capacity那么就扩容到n甚至更大如果小于那就什么都不做 //只是改变容量大小不变 void reserve(size_t n) {if (n _capacity){char* tmp new char[n 1];//多开一个存放‘\0’strcpy(tmp, _str,_size1);//连着‘\0’也一起拷贝delete[] _str;_str tmp;_capacity n;} } resize大小 规则如果n小于当前字符串长度那就缩短至n个字符的长度其他部分全部删除如果n大于当前长度那就扩大到n个字符的长度扩大的部分用特定的字符表示若未给出则默认给\0!所以要提供个缺省值 void resize(size_t n, char c \0)//半缺省 {if (n _size)//小于就缩到n个字符的长度{_str[n] \0;//字符串结尾都是以\0结尾_size n;}else{reserve(n);//扩容for (size_t i _size; i n; i){_str[i] c;//多出来的部分用特定字符补充}_str[n] \0;_size n;} } 六、修改 insert 我们熟知这个函数接口它的功能在pos位置前插入一个字符或者字符串并返回字符该字符位置既然是插入操作那必然会引起扩容问题这时我们又可以复用reserve // 在pos位置上插入字符c/字符串str并返回该字符的位置 string insert(size_t pos, char c) {assert(pos _size);//判断pos位置是否合法if (_size _capacity) //容量问题{reserve(_capacity 0 ? 4 : 2 * _capacity);}//这里有个细节那就是隐式类型提升//如果end的类型是size_t那么就会一直循环//如果是int的话因为pos是size_tend还是会隐式类型转化成size_t//所以对于这样的情况只能是将pos强转成int才能对pos0这样的情况处理/* int end _size;while (end (int)pos){_str[end 1] _str[end];--end;}*///写法二就不会出现上面的情况size_t end _size 1;while (end pos){_str[end] _str[end-1];//依次向后挪动数据--end;}_str[pos] c;_size;return *this; } 除了插入单个字符以外我们还可以插入字符串思路也是大差不差我们首先都是对pos位置的合法性进行判断然后再在要插入的位置后面的字符串整体向后挪动len个长度这里的len长度实际上就是你要插入的字符串的长度然后插入即可 string insert(size_t pos, const char* str) {assert(pos _size);//位置合法性size_t len strlen(str);//待插入的字符串长度//扩容问题if (_size len _capacity){reserve(_sizelen);}size_t end _size len;while (end poslen-1){_str[end] _str[end - len];--end;}strncpy(_str pos, str,len);_size len;return *this; } erase 规则擦除字符串中从字符位置pos开始并跨越len字符的部分(如果内容太短或len为string::npos则擦除直到字符串末尾) 从规则就可以看出它的实现首先就是需要判断位置是否合法如果合法那么长度是否达到要求。所要删除的字符串过长或者过短索处理的情况都是一样的 // 删除pos位置开始向后len个元素并返回该元素的下一个位置 string erase(size_t pos, size_t lennpos) {assert(pos _size);//位置的合法性判断if (len npos || len _size - pos)//长度的合法性判断{_str[pos] \0;//直接在pos位置加上‘\0’即可将后面的字符全部清除_size pos;}//长度和位置都合法直接用pos位置后面len个长度的字符覆盖前面即可else{strcpy(_str pos, _str pos len);//_size - len;}return *this; } 值得注意的是字符串是以\0结尾的所以要清楚后面的字符直接在特定的位置上加上\0即可 push_back 这个函数的作用就是在尾部插入一个字符实现起来十分的容易一个简单的尾插操作 void push_back(char c) {//写法一常规写法if (_size _capacity)//扩容问题{reserve(_capacity 0 ? 4 : 2 * _capacity);}_str[_size] c;_size;_str[_size] \0;//写法二复用inser函数insert(_size, c); } append 函数作用在尾部追加字符串 void append(const char* str) {//写法一常规写法size_t len strlen(str);if (_size len _capacity){reserve(_size len);}strcpy(_str _size, str);//将字符串拷贝到_size所指向的位置_size len;//写法二复用insertinsert(_size, str); } operator 这个函数的功能也是尾部追加不过它既可以追加字符也可以追加字符串实现也是十分的简单 //追加字符复用push_back string operator(char c) {push_back(c);return *this; }//追加字符串复用append string operator(const char* str) {append(str);return *this; } clear清理字符串内容 //不改变空间大小也就是_capacity不变 void clear() {_size 0;_str[_size] \0; } c_str 这个函数实际上将string对象转化为C类型的字符串底层十分简单就是返回_str即可 const char* c_str()const {return _str; } swap 这里的交换实际上是调用库里面的全局的swap进行交换但是要注意加上作用域限定符哦 void swap(string s) {//调用的是库里的全局交换函数std::swap(_str, s._str);std::swap(_size,s._size);std::swap(_capacity, s._capacity); } 七、查找 find 这个函数的功能从字符串pos位置不写默认是0开始往后找字符c/或者字符串返回的是第一个匹配的第一个字符的位置。如果没有找到匹配项函数返回string::npos-1。 // 返回c在string中第一次出现的位置 size_t find(char c, size_t pos 0) const//仅仅只是查找 {assert(pos _size);//位置的合法性判断for (size_t i pos; i _size; i)//遍历查找{if (c _str[i]){return i;}}return npos; } 当然了除了可以查找单个字符也可以查找一个字符串查找字符串底层我们使用的是strstr函数这个函数如果找到匹配的字符串会返回目标字符串起始的位置找不到就会返回空若找到那就用当前位置减去起始位置指针-指针就可以得到起始字符的下标 // 返回子串s在string中第一次出现的位置 size_t find(const char* s, size_t pos 0) const {assert(pos _size);const char* pstrstr(_strpos, s);if (p)//不为空找到{return p - _str;//指针-指针}else {return npos;} } substr左闭右开区间 功能从pos位置开始截取n个字符返回的结果是一个新的字符串对象这个新对象的内容就是使用截取到子字符串去初始化 string sub(size_t pos 0, size_t len npos) {string sub;//定义一个接受子串的对象//所截长度大于当前字符串的长度就截取到末尾if (len _size - pos){for (size_t i pos; i _size; i){sub _str[i];//将字符尾插到新对象即可}}//长度小于直接从pos位置开始截len个字符在尾插入新对象else{for (size_t i pos; i poslen; i){sub _str[i];}}return sub;//返回结果 } 八、非成员函数重载 swap全局string 在介绍string类时我们说过这个函数在库里面有两个一个是作为string类的成员函数(上面提到的)一个就是这个全局的swap但是要注意的是这个全局的swap不是在算法库里的算法库里的是一个模板 调用算法库里的模板 string s1(skkald); string s2(vckjn);swap(s1,s2);//调用算法库里的swap模板但是需要注意的是算法库的swap会拷贝构造一个临时对象C而后又进行两次赋值构造最后在析构临时对象C代价有点大而且在底层调试时实际走的是深拷贝也就是他会重新开出两块空间在交换一定程度上也有开销这也是为啥要在全局专门搞了一个string的swap的原因为的就是避免上述情况的发生那就相当于有三个swap成员函数string全局非成员函数算法库的 //string的非成员函数 void swap(string x, string y) {x.swap(y);//这里调用成员函数swap }string s1(skkald); string s2(vckjn);swap(s1,s2);//调用全局的string中swap也就是上面的代码 同时需要注意这里全局的swap和算法库里的模板swap并不会冲突因为模板中我们提到如果有现成的那么编译器会优先去调用现成函数没有的情况下才去考虑使用算法库的模板 关系运算符重载 这包含了!其实他们的底层实现需要用到就是C语言中的strcmp函数然后在进行相互之间的复用 bool operator(const string s1, const string s2) {int ret strcmp(s1.c_str(), s2.c_str());return ret 0;//两字符串相等就返回0 }bool operator(const strings1,const string s2) {int ret strcmp(s1.c_str(), s2.c_str());return ret 0;//第一个小于第二个就返回 }bool operator(const string s1, const string s2) {return s1 s2 || s1 s2;//复用上面的两个 }bool operator(const string s1, const string s2) {return (!(s1 s2)); } bool operator(const string s1, const string s2) {return (s1 s2) || (s1 s2); } bool operator!(const string s1, const string s2) {return !(s1 s2); } 重载 这个重载就使得我们能使用cout像内置类型那样输出底层实现十分的简单遍历就完事 ostream operator(ostream _out, const string s) {for (auto ch : s){_out ch;}return _out; } 重载 重载后便可以像内置类型那样去输入内容但是要先将对象字符串内容清空然后再依次读入直到遇到空格或者\n为止 写法一 istream operator(istream _cin, string s) {s.clear();//防止里面有脏数据char ch;ch _cin.get();//C中可以使用get获取一个字符while (ch ! || ch ! \n){s ch;//尾插ch _cin.get();}return _cin;//支持连续提取 } 其实上面的写法是有点缺陷的因为它涉及到空间消耗问题尾插必定会引起扩容所以上面的写法会频繁的去扩容但是如果先reserve那么要开多大的空间又是个问题太大万一字符串长度过小又浪费空间太小又可能会频繁扩容所以这时就诞生了第二种写法第二种写法的思路就是开个128大小的字符数组当字符个数到128时数组满直接尾插到字符串不足128时也不会消耗太大空间 写法二 istream operator(istream _cin, string s) {//消耗小的写法,需要多大就开多大这就不会频繁的去扩容空间也不会浪费s.clear();char buff[128];size_t i 0;char ch;ch _cin.get();while (ch ! || ch ! \n){buff[i] ch;if (i 127)//满128{buff[i] \0;s buff;i 0;}ch _cin.get();}//小于128if (i 0){buff[i] \0;s buff;}return _cin; } getline函数 这个函数的存在就是弥补cind的缺陷cin遇到空格就会停下,无法读取完整的一行而这个函数就是读完一行为止遇到空格不会停止 写法一 istream getline(istream in, string s) {s.clear();//防止里面有脏数据char ch;ch in.get();//C中可以使用get获取一个字符while (ch ! \n){s ch;//尾插ch in.get();}return in;//支持连续提取 } 写法二 istream getline(istream in, string s) {//消耗小的写法,需要多大就开多大这就不会频繁的去扩容空间也不会浪费s.clear();char buff[128];size_t i 0;char ch;ch in.get();while (ch ! \n){buff[i] ch;if (i 127)//满128{buff[i] \0;s buff;i 0;}ch in.get();}//小于128if (i 0){buff[i] \0;s buff;}return in; }
http://www.zqtcl.cn/news/661598/

相关文章:

  • 个人建站如何赚钱男人的好看网
  • 门户网站建设管理工作作一手房用什么做网站
  • 网站建设优化服务案例三合一网站程序
  • 网站长尾词关于制作网站的方案
  • 做二手衣服的网站有哪些wordpress单本小说采集
  • 曲靖市建设局网站品牌营销咨询公司是做什么的
  • wordpress网站统计代码放哪个文件putty搭建wordpress
  • 桦南县建设局网站天坛装修公司口碑怎么样
  • 网站的建设求职简历网站开发与维护价格
  • 网站空间备份站长网站优点
  • 房产网站做那个比较好网页设计属于前端吗
  • 衡水企业网站建设费用html5网页设计教程
  • 用wp系统做网站网站有收录没排名
  • 网站源码程序下载ios开发软件
  • 设计好的网站什么是企业网站策划案
  • 北京网站建设亿玛酷适合5传奇网站装备动态图怎么做
  • 多平台网站设计实例3d效果图什么网站做的好
  • 58同城西安网站建设购物网站前端浮动特效怎么做
  • asp网站模板源码wordpress 画图插件
  • 免费网站建站 知乎伪原创嵌入网站
  • 2网站建设城乡住房建设网站
  • 游戏网站建设公司建设银行网站登陆二星是什么意思
  • 长春网站排名优化泉州网站建设方案服务
  • 教育培训机构加盟十大排名搜索引擎优化宝典
  • 全景精灵网站建设网站建设长尾关键词
  • 老城网站建设注册网站不需要手机验证的
  • 可以赚钱做任务的网站有哪些莘县做网站
  • 可信网站 认证规则山东网站建设代理
  • 网站怎么谈设计常用的软件开发文档有哪些
  • 该怎么给做网站的提页面需求焦作做网站公司