网站建设引言网站的特点有那些

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

网站建设引言,网站的特点有那些,凡科网,建设一个网站需要多少费用Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏#xff1a; 与C的邂逅 本篇博客我们将来了解string容器本身以及接口的使用。 string是串#xff0c;本质是一个字符数组#xff0c;可以对其进行增删查改。 s Code World (๑•́ ₃ •̀๑) 个人主页:        9ilk (๑•́ ₃ •̀๑) 文章专栏     与C的邂逅    本篇博客我们将来了解string容器本身以及接口的使用。 string是串本质是一个字符数组可以对其进行增删查改。 string构造 //常用 string s1; string s2(hello world); string s3(s2);//不常用 string s4(s2,3,5); // string (const string str, size_t pos, size_t len npos) string s5(s2,3); string s6(s2,3,30); string s7(hello world,5) //string (const char* s, size_t n) string s8(10,x); //string (size_t n, char c); 对于string的构造我们常用的是它的无参构造拷贝构造等。注意在库中pos通常代表位置n代表的是个数。 npos 由库中文档说明,我们可以知道 :

  1. npos是string里的一个const静态成员变量,类型是size_t,缺省值给的是-1(整形最大).
  2. 当使用substring的这个构造时,从str的pos位置开始拷贝构造,如果要构造的长度len大于后面的长度,则有多少拷贝多少直到拷贝到结尾 .
  3. 缺省参数npos是整形最大值,一定大于后面的长度,不传第三个参数默认拷贝到结尾. string s2(hello world); // string (const string str, size_t pos, size_t len npos) string s4(s2,3,5); //拷贝5个字符 string s5(s2,3); //拷贝到结尾 string s6(s2,3,30);//拷贝到结尾 隐式类型转换在构造中的应用 string s1(hello world); //隐式类型转换 string s2 hello world; const string s3 hello world;//以及后面其他接口也可以使用… push_back(hello world); 说明 : 1. 对于s2是先调用常量字符串的构造生成一个临时对象,再调用拷贝构造将临时对象拷贝给s2. 2. 对于s3,它引用的是临时对象,而临时对象具有常性,因此需要是const引用. 元素访问方式 operator[ ] char operator; const char operator const; 引用返回 int main() {string s hello;for (size_t i 0; i s.size(); i){cout s.operator ; //显式[]下标访问对象元素cout s[i] ;//隐式[]下标访问对象元素}for (size_t i 0; i s.size(); i){s[i]; //修改访问元素内容}return 0; } 由于string类重载了[ ]运算符,所以我们可以直接利用[ ] 下标访问对象中的元素;同时由于是引用返回,所以我们可以通过[ ] 下标修改对应位置的元素内容,也就是可读可写. class string { /… private:char* _str;size_t _size;int _capacity; }char operator {assert(i _size);return _str[i]; } string类一般是动态开辟内存来储存字符元素,所以实现operator[ ]一般返回的是在堆上的空间,因此我们不用担心传引用返回时返回的是一个局部对象. 传引用返回优势 :  1. 能修改返回对象. 2. 减少拷贝. string类重载的[]运算符还会执行严格的越界检查,只要下标超过size或小于0就报错.opertaor[ ]有两个版本,一个是非const,返回对象可修改,是典型的可读可写;另一个是const版本,返回对象内容不可修改. const string s2(hello); s2[0] x; //不可修改 调用const版本的operator[]string s1(hello); s1[0] x;//调用非const版本的opeator[]1. const版本重载的[ ]主要是给const对象用的,毕竟const对象无法调用非const版本,这是权限的扩大. 2. 对于const和非const对象其实都能调用const版本的重载,但却重载出两个版本,这是因为各自有不同的需求(比如只有一个const版本的operator[ ],对于普通string对象就不能修改了,不符合我们的要求,如果重载了两个版本则调用更匹配的,普通对象调用普通版本,const对象调用const版本). 3.具体实践中重载[]操作符看具体需求. 迭代器 迭代器目前我们可以理解为具有和指针类似行为的东西,但实际上不一定是指针. string::iterator it s.begin(); while (it ! s.end()) {cout *it endl; //类似指针解引用it;//类似指针 } auto it s.begin(); 迭代器访问元素是所有容器(除stack,queue外)通用的访问方式,他们通用的将迭代器命名为iterator,头元素位置的迭代器和尾元素位置的迭代器都命名为begin和end,它们都是定义在类域里的成员函数. 注 : C11之后直接使用auto定义迭代器让编译器推到迭代器的类型. for (auto ch : s)cout ch endl; C11之后我们更喜欢用范围for来访问string类元素,它底层也是利用的迭代器. begin与end 对于begin返回的是开始位置,对于end返回的是最后一个字符的下一个位置,在string类中大概就是\0的位置. 迭代器的版本 const string s1(hello); string::const_iterator it1 s1.begin(); while (it1 ! s1.end()) {cout it1 endl;//无法修改 }迭代器分为iterator(可读可写)和const_iterator(只读),对于const对象匹配的是返回const_iterator的begin().下面我们来区分const iterator , iterator 和 const_iterator. 类型特点类比指针iterator可读可写int p1const_iterator只读 迭代器指向数据不能写const int* p2const iterator迭代器本身不能写int* const p3 反向迭代器 string::reverse_iterator it2 s1.rbegin(); while (it2 ! s1.rend()) {it2 3;it2; } 反向迭代器是从后往前遍历,rbgein就是返回的原来正向最后一个元素位置(\0前面),rend返回的是原来正向第一个元素的前一个位置. 注 : string类通过运算符重载改变了反向迭代器和–的行为,底层是封装的正向迭代器. 总结 : 迭代器一共有四种:1. iterator的const和非const版本 以及 reverse_iterator的const和非const版本. 注 : 库里面其实还有cbegin和cend,他们其实就是类似iterator的const版本. at string s1(hello); try {s1[10]; } catch (const exception e) {cout e.what() endl; }string s(hello world); for (size_t i 0; i s.size(); i) {//at(pos)访问pos位置的元素cout s.at(i); } cout endl; for (size_t i 0; i s.size(); i) {//at(pos)访问pos位置的元素并对其进行修改s.at(i) x; } cout s endl; //xxxx at是访问pos位置的字符,它也是引用返回,可读可写;与operator[ ]不同的是,operator[ ]对于下标是暴力检查,而at是抛异常,两者底层处理越界的方式不同. 总结: string类元素遍历方式主要有4种: 1. [ ] 下标 2. 使用at访问对象中的元素 3. 迭代器遍历 4.范围for 我们实践中主要用的是[]以及范围for. 修改 尾部插入和尾部删除 尾部插入 push_back() void push_back (char c); //应用 string s(hello); s1.push_back(x); 我们可以使用push_back来尾部插入字符。 append() string append (const string str); string append (const char s); string append (size_t n, char c);//应用 string s1(hello); string s2( world);//append(string)完成两个string对象的拼接 s1.append(s2); //hello world //append(str)完成string对象和字符串str的拼接 s1.append( C); //hello world C//append(n, char)将n个字符char拼接到string对象后面 s1.append(3, !); //hello world C!!!cout s1 endl; //hello world C!!!我们可以使用append来实现尾部插入string对象/字符串/多个相同字符这里也体现了vector与string的不同vectorchar一次只能进一个char字符而string对象一次可能进一个串。 operator string类重载的可以一个字符/串/string对象我们在实践中更多使用来尾部插入。 string s1(hello); string s2( world);s1 s2; //string s1 C ; //字符串 s1 !; //字符 尾部删除 void pop_back();string str (hello world!); str.pop_back(); cout str \n; //hello world 我们可以用pop_back来删除string对象的尾部字符。 insert和erase insert string insert (size_t pos, const string str); string insert (size_t pos, const char* s); iterator insert (iterator p, char c); template class InputIterator void insert (iterator p, InputIterator first, InputIterator last); insert可以帮助我们在pos位置(下标/迭代器)插入字符可以插入一个string对象/字符串/字符。注意是在pos位置插入 string s©; //C//insert(pos, str)在pos位置插入字符串strs.insert(1, H); //CH//insert(pos, string)在pos位置插入string对象string t(I);s.insert(2, t); //CHI//insert(pos, char)在pos位置插入字符chars.insert(s.end(), N); //CHIN//nsert (iterator p, InputIterator first, InputIterator last)//在p位置插入另一个迭代区间string h(A);s.insert(s.end(),h.begin(),h.end());cout s endl; //CHINA注  
  4. 使用insert时我们要注意传入区间和下标的合法性。
  5. 一次insert的时间复杂度是O(N),一段程序中不断对string对象调用insert会有较大的时间消耗因此在实践中我们要慎用。
  6. insert其实可以复用来实现尾插头插。 erase string erase (size_t pos 0, size_t len npos); iterator erase (iterator p); iterator erase (iterator first, iterator last); erase可以帮助我们实现在指定位置删除后面指定长度len同样地如果不传npos或传的len大于剩下的字符串长度则有多少删多少。 string s(hello); s.erase(0, 1); cout s endl; //输出ellos.erase(0, 20); cout s endl;//输出空串 注 erase也是一样要慎用效率不高也是O(N) assign string assign (const string str); string assign (const char* s);//应用 //const char* str string s1(hello world); s1.assign(yes); cout s1 endl; //输出yes //string string s2(ha); s1.assign(s2); cout s1 endl; //输出ha assign其实是变相的赋值把原来的内容清空再赋值成新的内容。 replace string replace (size_t pos, size_t len, const string str); string replace (size_t pos, size_t len, const char* s); string replace (size_t pos, size_t len, size_t n, char c); replace接口的作用是把原串的某一段内容替换位为字符/字符串。 //string replace (size_t pos, size_t len, const string str); string s(hellea); s.replace(4,1,oworld);// helloworlda cout s endl;// string replace (size_t pos, size_t len, size_t n, char c); s hellea; s.replace(4,1,2,o); cout s endl;//hellooa 注  
  7. len表示的是从pos位置开始要替换的字符个数。
  8. replace如果替换字符多的话效率不高也需要慎用。 string s(hello world hello bit); for (int i 0; i s.size() ;) {if (s[i] .){s.replace(i, 1, %20);i 3;}elsei; } 我们可以用replace实现替换空格但是这样效率不高我们可以利用一个新的string对象提高效率,这样不用挪数据到新串里。 string s(hello world hello bit); string s1;//空串 for(auto ch : s) {if(ch ! )s1 ch; elses1 %20; }operator   string类中对运算符进行了重载重载后的运算符支持以下几种类型的操作 1.string类 string类 2.string类 字符串 3.字符串 string类 4.string类 字符 5.字符 string类它们相加后均返回一个string类对象。 string s;string s1(super);string s2(man);char str[] woman;char ch !;//string类 string类s s1 s2;cout s endl; //superman//string类 字符串s s1 str;cout s endl; //superwoman//字符串 string类s str s1;cout s endl; //womansuper//string类 字符s s1 ch;cout s endl; //super!//字符 string类s ch s1;cout s endl; //!super 注 operator重载参数必须要有一个自定义类型因此不能字符串字符串。 容量 size()与length() string s1 hello; cout s1.size() endl; //输出5 cout s1.length() endl; 说明 :
  9. 对于size和length都是求字符串的长度.length是主要针对串这个容器而设计的,而size是面对大多数容器设计的表示容器元素个数的接口.
  10. 对于size和length,两个算出的字符串长度都是不算\0的 !  capacity size_t capacity() const; 使用capacity函数获取当前对象所分配的存储空间的大小。 string s(CSDN); cout s.capacity() endl; //15 关于扩容方式的探讨 void TestPushBack() {string s;size_t sz s.capacity();cout making s grow:\n;for (int i 0; i 100; i){s.push_back©;if (sz ! s.capacity()){sz s.capacity();cout capacity changed: sz \n;}} } vs2019环境输出结果 capacity changed:15 making s grow: capacity changed:31 capacity changed:47 capacity changed:70 capacity changed:105 capacity changed:157 capacity changed:235 g环境输出结果 capacity changed: capacity changed: 2 capacity changed: 4 capacity changed: 8 capacity changed: 16 capacity changed: 32 capacity changed: 64 capacity changed: 128 capacity changed: 256 注意:
  11. 严格来说capacity比实际空间少一个,有一个多的是预留给\0的。 2.如何扩容C标准并没有规定取决于编译器的实现。比如在vs2019环境下第一次是二倍扩容后面都是1.5倍扩容而g环境下一直是2倍扩容。 max_size size_t max_size() const; 使用max_size函数获取string对象对多可包含的字符数。 string s(CSDN); cout s.max_size() endl; //4294967294 clear void clear(); clear的作用是把数据全部清理掉它只是把string有效字符清空成为空串只剩下\0而不改变底层空间的大小。 //vs2019环境 string s1 hello; cout s1.size() endl;//5 cout s1.capacity() endl;//15 s1.clear(); cout s1.size() endl; //输出 0 cout s1.capacity() endl;// 15 empty bool empty() const; 使用empty判断对象是否为空。 string s(CSDN); cout s.empty() endl; //0//clear()删除对象的内容该对象将变为空字符串 s.clear(); cout s.empty() endl; //1 reserve和resize 使用resize改变当前对象的有效字符的个数 void resize (size_t n); void resize (size_t n, char c); resize注意事项
  12. resize会改变对象的capacity和size。 2.当n大于对象当前的size时将size扩大到n扩大的字符为c若c未给出则默认为’\0’。 3. 当n小于对象当前的size时将size缩小到n缩不缩容取决于编译器一般空间不变。 4. 若给出的n大于对象当前的capacity则capacity也会根据自己的增长规则进行扩大。 string s1(hello); s1.resize(6); //hello\0\0 s1.resize(20,x);//helloxxxxxxxxxxxxxxx//扩大 cout s1.size() endl; //20 cout s1.capacity() endl; //31 //缩小 s1.resize(10); cout s1.size() endl; //10 cout s1.capacity() endl; //31 使用reserve改变当前对象的容量大小 void reserve (size_t n 0); string s(hello); cout s.capacity()endl; //15 //扩容 s.reserve(100); cout s.capacity()endl; //111 //缩容 s.reserve(20); cout s.capacity()endl; //111 s.reserve(10); cout s.capacity()endl;//10 我们可以得到在vs环境下扩容是会多扩的而缩容传参数比capacity小是默认不会缩容的小于15才会缩。 string s(hello); cout s.capacity()endl; //5 //扩容 s.reserve(100); cout s.capacity()endl; //100 //缩容 s.reserve(20); cout s.capacity()endl; //20 s.reserve(10); cout s.capacity()endl;//10 linux环境下,reserve扩容是要多少扩多少;缩容是要缩多少就缩到多少。 注
  13. reserve改变的是capacity而不能改变size。 2.reserve改变空间(为string预留空间)但不改变size(有效元素个数)要注意越界问题。 string s1(hello); s1.resize(200); s1[200]; //越界 3.由于扩容和缩容在不同平台的不确定性所以我们最好用reserve来提前开好我们需要的空间因此reserve的使用场景是知道需要提取开多少空间。 shrink_to_fit void shrink_to_fit(); string s1(100,x); cout s1.capacity() endl;//111 cout s1.size() endl;//100 s1.resize(10); s1.shrink_to_fit(); cout s1.capacity() endl;//15 cout s1.size() endl;//10 shirink_to_fit可以使size和capacity同步变化但不要经常调用因为实际的缩容是开新空间释放旧空间以时间换空间代价较大。 算法与子串的提取 算法 sort算法 库里的排序算法传参数是随机迭代器区间(左闭右开)它可以对大部分容器(不能对list排序)进行排序相当于是一个函数模板利用迭代器这个桥梁将算法与容器连接起来。 string s1(hello);//按字典序排序 全部排 sort(s1.begin(),s1.end()); //ehllo //第一个和最后一个不参与排序 sort(s1.begin(),–s1.end()); //hello //前4个排序 sort(s1.begin(),s1.begin()4); //ehllo 注 对于string容器使用sort排序是按字典序排序同时它可以对整个数据处理也可以对容器的一部分数据处理。 reverse算法 算法库里的reverse也是一个函数模板能对容器里的数据实现逆置参数是迭代器区间。 string s(hello); reverse(s.begin(),s.end()); cout s endl; //olleh 注 它也可以实现完全逆置和部分逆置但是它和sort都需要包含algorithm这个头文件。 子串的提取 使用substr函数提取string中的子字符串。 string substr (size_t pos 0, size_t len npos) const; 注 substr是pos位置开始生成len长度的子串len过大或不传就是后面剩下的字符都变成子串采取的也是左闭右开的迭代器区间。 string s1(abcdef); string s2;//substr(pos, n)提取pos位置开始的n个字符序列作为返回值 s2 s1.substr(2, 4); cout s2 endl; //cdef 使用copy函数将string的子字符串复制到字符数组中。 size_t copy (char* s, size_t len, size_t pos 0) const; string s(abcdef); char str[20];//copy(str, n, pos)复制pos位置开始的n个字符到str字符串 size_t length s.copy(str, 4, 2); //copy函数不会在复制内容的末尾附加\0需要手动加 str[length] \0; cout str endl; //dcef注
  14. copy返回值是赋值的len。 2.copy函数不会在复制内容的末尾附加\0需要我们手动加上。 查找 find 使用find函数正向搜索第一个匹配项可以在string对象里查找一个字符/字符串/string对象返回的是第一个匹配的位置未找到则返回-1. size_t find (const string str, size_t pos 0) const; size_t find (const char* s, size_t pos 0) const; size_t find (char c, size_t pos 0) const;string s1(http://www.cplusplus.com/reference/string/string/find/);//find(string)正向搜索与string对象所匹配的第一个位置 string s2(www); size_t pos1 s1.find(s2); cout pos1 endl; //7//find(str)正向搜索与字符串str所匹配的第一个位置 char str[] cplusplus.com; size_t pos2 s1.find(str); cout pos2 endl; //11//find(char)正向搜索与字符char所匹配的第一个位置 size_t pos3 s1.find(:); cout pos3 endl; //4rfind 使用rfind函数反向搜索第一个匹配项rfind是倒着找一个字符/字符串/string对象如果存在返回匹配的第一个位置否则返回-1. size_t rfind (const string str, size_t pos npos) const; size_t rfind (const char* s, size_t pos npos) const; size_t rfind (char c, size_t pos npos) const;string s1(http://www.cplusplus.com/reference/string/string/find/);//rfind(string)反向搜索与string对象所匹配的第一个位置 string s2(string); size_t pos1 s1.rfind(s2); cout pos1 endl; //42//rfind(str)反向搜索与字符串str所匹配的第一个位置 char str[] reference; size_t pos2 s1.rfind(str); cout pos2 endl; //25//rfind(char)反向搜索与字符char所匹配的第一个位置 size_t pos3 s1.rfind(/); cout pos3 endl; //53查找系列 size_t find_first_of (const string str, size_t pos 0) const; size_t find_first_of (const char* s, size_t pos 0) const; //其他类似…std::string str (Please, replace the vowels in this sentence by asterisks.); std::size_t found str.find_first_of(aeiou); while (found!std::string::npos) {str[found];foundstr.find_first_of(aeiou,found1); } std::cout str \n; 输入与输出 operator 和 operator   string类中也对和运算符进行了重载这就是为什么我们可以直接使用和对string类进行输入和输出的原因。 ostream operator (ostream os, const string str); istream operator (istream is, string str); string s; cin s; //输入 cout s endl; //输出 getline istream getline (istream is, string str, char delim); istream getline (istream is, string str); 如果我们想输入一串含有空格的字符串到string对象中使用cin是不行的因为cin默认规定空格或换行是多个值之间分割也就是当读取到空格便会停止读取。 string s; getline(cin, s); //输入hello CSDN cout s endl; //输出hello CSDN getline函数将从is中提取到的字符存储到str中直到读取到换行符’\n’为止。 string s; getline(cin, s, D); //输入hello CSDN cout s endl; //输出hello CS getline函数将从is中提取到的字符存储到str中直到读取到分隔符delim或换行符’\n’为止。 string与其他数据类型转换 to_string : 其他数据类型转为字符串 库中提供了将其他数据类型转化为字符串的函数,但有时要注意数据溢出转换失败的风险。 int x,y 0; cin x y; //输入 20 30 string str to_string(xy); cout str endl; //输出50 sto.. : 字符串转化为其他数据类型。 注 注意数据溢出的风险。 string与字符串的转化 将字符串转换为string //方式一 string s1(hello world);//方式二 char str[] hello world; string s2(str);cout s1 endl; //hello world cout s2 endl; //hello world使用c_str或data将string转换为字符串 1. c_str作用是获取底层的ptr指针它是为了更好的与c语言兼容。 2.data与c_str类似就跟size与length的关系。 3.在C98中c_str()返回 const char 类型返回的字符串会以空字符结尾在C98中data()返回 const char* 类型返回的字符串不以空字符结尾 4. 在C11版本中c_str()与data()用法相同 string s(hello world ); const char* str1 s.data(); const char* str2 s.c_str();cout str1 endl; cout str2 endl;// string file(Test.cpp); cout file.c_str() endl; vs和g下string结构的不同 注意下述结构是在32位平台下进行验证32位平台下指针占4个字节。 vs下string的结构 string总共占28个字节内部结构稍微复杂一点先是有一个联合体联合体用来定义string中字符串的存储空间 1.当字符串长度小于16时使用内部固定的字符数组来存放。 2.当字符串长度大于等于16时从堆上开辟空间。 union _Bxty { // storage for small buffer or pointer to larger one value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // to permit aliasing } _Bx 这种设计也是有一定道理的大多数情况下字符串的长度都小于16那string对象创建好之后内部已经有了16个字符数组的固定空间不需要通过堆创建效率高。 其次还有一个size_t字段保存字符串长度一个size_t字段保存从堆上开辟空间总的容量 最后还有一个指针做一些其他事情。 故总共占1644428个字节。 g下string的结构 G下string是通过写时拷贝实现的string对象总共占4个字节内部只包含了一个指针该指针将来指向一块堆空间内部包含了如下字段: 1. 空间总大小 2.字符串有效长度 3.引用计数 4.指向堆空间的指针用来存储字符串 struct _Rep_base {size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount; }; 关于引用计数和写时拷贝: 对于浅拷贝会遇到两个问题:1. 由于两个string对象指向同一块空间,因此在析构两个对象时,会释放两次空间。 2. 由于两个对象指向同一块空间因此一个对象修改数据会影响另一个对象。 解决方法 1. 引用计数  当每次为分配内存时我们总是要多分配一个内存空间用来存放这个引用计数的值只要发生拷贝构造和赋值时这个内存值就会1当计数不为1时不销毁空间由最后一个析构的对象释放空间。 2. 写时拷贝在内容修改时string类查看这个引用计数是否为1如果不为1表示有人在共享这块内存此时需要做一份深拷贝.也就是说如果对这块内存不写就没有深拷贝只有浅拷贝不修改就赚了效率很高稳赚不赔。 relational operators 与 赋值 relational operators string类中还对一系列关系运算符进行了重载它们分别是、!、、、、。重载后的关系运算符支持string类和string类之间的关系比较、string类和字符串之间的关系比较、字符串和string类之间的关系比较。 string s1(abcd); string s2(abde); cout (s1 s2) endl; //0 cout (s1 s2) endl; //1 cout (s1 s2) endl; //0 注意这些重载的关系比较运算符所比较的都是对应字符的ASCII码值。 赋值 string类中对运算符进行了重载重载后的运算符支持string类的赋值、字符串的赋值以及字符的赋值。 string s1; string s2(CSDN);//支持string类的赋值 s1 s2; cout s1 endl; //CSDN//支持字符串的赋值 s1 hello; cout s1 endl; //hello//支持字符的赋值 s1 x; cout s1 endl; //x完。