string

模板:

泛型编程

函数模板

 模板关键字:tmplate<calss T> //T可以随意改
 template<class T> //template<typename T> 模板参数列表 - 参数类型
 void Swap(T& x1, T& x2) //函数参数列表 - 参数对象
 {
 T x = x1;
 x1 = x2;
 x2 = x;
 }
 传入不同的类型 会调用不同的实例化函数
 多个类型:
     template<class T1,class>

模板实例化

调用Swap时 会实例化出不同类型的函数
实例化:是指在面向对象的编程中,把用类创建对象的过程称为实例化。 是将一个抽象的概念类,具体到该类实物的过程。 实例化过程中一般由类名对象名= new 类名(参数1,参数2...参数n)构成。

函数模板的实例化

 template<class T>
 T Add(const T& left, const T& right)//做返回值 和
 {
     return left + right;
 }
 int main()
 {
     int a1 = 10, a2 = 20;
     double d1 = 10.1, d2 = 20.2;
     cout << Add(a1, a2) << endl;
     cout << Add(d1, d2) << endl;
     
     cout << Add((double)a1, d2) << endl;
     
     //显示实例化
     cout << Add<int>(d1, d2) << endl; //指定类型
     cout << Add<double>(d1, d2) << endl; 
     
     return 0;
 }
 结果:
 30
 30.3
 30.2
 30
 30.3

如果函数模板 和 普通函数 同时存在,优先使用普通函数

类模板

一个栈类型 只能是 int 或 double 或...
定义对象只能满足 int栈 或者double栈
想要存储多个类型 则需要建多个不同类型的 类

 template<class T>
 class Stack
 {
 privvate:
     T* _a;
     int _top;
     int _capacity;
 }
 int main()
 {
     Stack<int> st1;//存储int
     Stack<double> st2;//存储double
 }

STL:

S标准T模板L库
Vue - 前端贡献:尤雨溪

书籍推荐

《STL源码剖析》《effcrive C++》 继承多态学完看 《高质量C++》现在看

string

c++文档:

https://cplusplus.com/

string

编码 - 值 -- 符号建立映射
ASCII码表 - 表示英文编码表
unicode - 表示全世界文字编码表 utf-8
gbk - 中文编码表

常用的string

  • 赋值
default (1)string();
copy (2)string (const string& str);
substring (3)string (const string& str, size_t pos, size_t len = npos);
from c-string (4)string (const char* s);
  • size:计算长度
    Return length of string (public member function )
  • capacity:计算空间大小 不算\0 所以是15
    Return size of allocated storage (public member function )
  • clear:清除string内容
    Clear string (public member function )
  • reserve
    Request a change in capacity (public member function )
  • [operator[ ]](https://cplusplus.com/reference/string/string/operator%5B%5D/):出错断言assert
    Get character of string (public member function )

           char& operator[] (size_t pos);
     const char& operator[] (size_t pos) const;
     这里的引用返回时为了 修改返回对象
     读取
     s1[i] = s1.operator[]  的重载
    lesson8string类01
     修改
     s1[i]+=1;
    lesson8string类02
  • at:出错抛异常
    Get character in string (public member function)
  • operator+=:尾插字符或字符串
    Append to string (public member function)

     string s1;
     s1 += ':';
     s1 += "hello world";
     cout << s1 << endl;
  • append:插入字符串 不常用
    Append to string (public member function)
  • push_back:插入字符串 不常用
    Append character to string (public member function)
 string s1;
 s1.push_back('a');
 s1.append("bcde");
 cout << s1 << endl;

string 迭代器

 //遍历+修改
 //方式1:下标+[]  
 //返回对应位置的引用  可以直接修改s1[i]
 for (size_t i = 0; i < s1.size(); ++i)
 {
 s1[i] += 1;
 }
 for (size_t i = 0; i < s1.size(); ++i)
 {
 cout << s1[i] << " ";
 }
 cout << endl;

迭代器想象成:像指针一样的类型

end()/begin()

       iterator begin();
 const_iterator begin() const;
 
       iterator end();
 const_iterator end() const;
 string s1("hello world");
 //方式2:迭代器(iterator)
 //begin()指的是第一个元素的位置  end()是最后一个元素的【下一个】
 //it像指针一样指向第一个元素 ,可以解引用,可以++
 string::iterator it = s1.begin();
 while (it != s1.end())
 {
     *it -= 1;
     ++it;
 }
 it = s1.begin();//重置it的位置
 while (it != s1.end())
 {
     cout << *it << " ";
     ++it;
 }
 cout << endl;

iterator begin();

范围for会替换成迭代器

 //方式3:范围for   语法糖用起来很爽很甜
 // C++11   linux:-std=c++11
 //把s1中的值取出来 赋值给e 自动++
 //for (char& e : s1) 
 for (auto& e : s1) //引用  
 {
     e += 1;
 }
 for (auto e:s1)
 {
     cout << e << " ";
 }
 cout << endl;

const_iterator begin() const;

const 版本只能读取 不能修改

 void func(const string& s1)
 {
 string::const_iterator it = s1.begin();
 //auto it = s1.cbegin(); //cbegin()和cend()代表const
 while (it != s1.end())
 {
 //*it -= 1; //不能修改
 cout << *it << " ";
 ++it;
 }
 cout << endl;
 }

rbegin() / rend(): 反向迭代器

       reverse_iterator rbegin();
 const_reverse_iterator rbegin() const;
  • rbegin() 最后一个字符
  • rebing()的 ++ 是向前走
  • rend()是第一个字符的前一个
 void test_string2()
 {
 string s1("hello world");
 //反向迭代器
 //rbegin() 指向最后一个字符 
 //
 //反向++ 是逆向的
 //string::reverse_iterator rit = s1.rbegin(); // string::reverse_iterator是类型
 auto rit = s1.rbegin();//代替上面那句自动推到类型
 while (rit != s1.rend())
 {
 cout << *rit << " ";
 ++rit;
 }
 cout << endl;
 
 string cstr("hello world");
 func(cstr);
 }

c++11 新增const迭代器

迭代器的意义是什么?

所有的容器都可以使用迭代器这种方式去访问修改

答:

对于string,下标和[]就足够好用,确实可以不用迭代器。

其他容器(数据结构)呢?

(list、map/set 并不支持下标,只有数组才支持[],这些是链表和二叉树并不支持下标+[])

所以迭代器才是通用的方式

string 增容

测试代码

 void TestPushBack()
 {
 string s;
 //s.reserve(1000);//申请至少能存储10000个字符的空间  不一定是1000 要空间对其
 
 size_t sz = s.capacity();
 cout << "capacity changed: " << sz << '\n';
 cout << "making s grow:\n";
 for (int i = 0; i < 2000; ++i)
 {
 s.push_back('c');
 if (sz != s.capacity())
 {
 sz = s.capacity();
 cout << "capacity changed: " << sz << '\n';
 }
 }
 }
结果
 capacity changed: 15//本质是16 但是没算\0  有效字符位置有15个
 making s grow:
 capacity changed: 31//本质是32 但是没算\0  有效字符位置有31个
 capacity changed: 47
 capacity changed: 70
 capacity changed: 105
 capacity changed: 157
 capacity changed: 235

reserve:扩容,只开空间

resize:扩容+初始化

开空间,并给初始值 进行初始化

如果resize扩容比已有数据少,则会删除多余数据

 void test_string3()
 {
 string s1;
 s1.reserve(100);
 //开空间并初始化
 string s2;
 //s2.resize(100); // 初始化的 \0
 s2.resize(100,'x');//指定字符初始化
 //resize不会把已有数据覆盖初始化,如果比已有数据小,则会除多余的数据
 }

string 查找

c_str返回C格式字符串

 const char* c_str() const;
 void test_string4()
 {
 string s("hello world");
 cout << s << endl;//流插入  size是多少 打印多少
 cout << s.c_str() << endl;//const char*  遇到\0结束
     
     //应用场景 这里fopen的第一个参数需要const char*类型的字符串
     string file("test.txt");
 FILE* fout = fopen(file.c_str(), "w");
 }

find:找位置

从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置

string (1)size_t find (const string& str, size_t pos = 0) const;
c-string (2)size_t find (const char* s, size_t pos = 0) const;
buffer (3)size_t find (const char* s, size_t pos, size_t n) const;
character (4)size_t find (char c, size_t pos = 0) const;

npos:-1

size_t类型的 -1 一个很大的数

substr:查找字符串

在str中从pos位置开始,截取n个字符,然后将其返回

 string substr (size_t pos = 0, size_t len = npos) const;
 string file("test.txt");
 FILE* fout = fopen(file.c_str(), "w");
 size_t pos = file.find('.');
 if (pos != string::npos)//npos是 -1  size_t全1
 {
     //string suffix = file.substr(pos, file.size() - pos);
     string suffix = file.substr(pos);//直接用默认缺省值 取到最后
     cout << suffix << endl;
 }

如果是连续后缀 ,要从右往左找

rfind反向找

 string file("test.txt.zip");
 FILE* fout = fopen(file.c_str(), "w");
 size_t pos = file.rfind('.');//这里用rfind
 if (pos != string::npos)
 {
 string suffix = file.substr(pos);
 cout << suffix << endl;
 }
 }
  • 解析URL

     string url("https://m.cplusplus.com/reference/string/string/rfind/");
     //取协议
     size_t pos1 = url.find(':');
     string protocol = url.substr(0, pos1 - 0);
     cout << protocol << endl;
     //取域名
     size_t pos2 = url.find('/',pos1+3);//冒号+3是w的位置开始找/ 
     string domain = url.substr(pos1 + 3,pos2-(pos1+3));
     cout << domain << endl;
     //取路径
     string uri = url.substr(pos2 + 1);//从域名后的/ +1 的位置找到最后
     cout << uri << endl;
     
     https
     m.cplusplus.com
     reference/string/string/rfind/

string 插入删除

insert插入

string (1)**string& insert (size_t pos, const string& str);
substring (2)string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
c-string (3)string& insert (size_t pos, const char* s);
buffer (4)string& insert (size_t pos, const char* s, size_t n);
fill (5)**string& insert (size_t pos, size_t n, char c); void insert (iterator p, size_t n, char c);
single character (6)**iterator insert (iterator p, char c);
range (7)template <class InputIterator> void insert (iterator p, InputIterator first, InputIterator last);
 void test_string5()
 {
 string s("hello world");
 s += ' ';//尾插
 s += "!!!";
 cout << endl;
 //头插 效率低 O(N) 尽量少用
 s.insert(0, 1, 'x');//在0的位置插入1个x
 s.insert(s.begin(), 'y');//在头部插入y
 s.insert(0, "test");//在0的位置插入test
 cout << s << endl;
 //中间位置插入,尽量少用
 s.insert(4, "&&&&&&");
 cout << s << endl;
 }

erase删除

sequence (1)**string& erase (size_t pos = 0, size_t len = npos);
character (2)iterator erase (iterator p);
range (3)iterator erase (iterator first, iterator last);
 void test_string6()
 {
 string s("hello world");
 //尽量少用头部和中间的删除,效率低
 s.erase(0,1);//删除头上的一个字符
 s.erase(s.size()-1,1);//删除尾部的一个字符
 cout << s << endl;
     s.erase(3);//从第三个位置 后面全部删除
 cout << s << endl;
 }

operator+

relational operators比较大小

getline连续获取一行字符串

方法一的代码就是getline的原理,一个字符一个字符的获取!

(1)istream& getline (istream& is, string& str, char delim);
(2)istream& getline (istream& is, string& str);

字符串里面最后一个单词的长度

 #include <iostream>
 using namespace std;
 
 int main()
 {
     string s;
     //cin>>s;//cin读到空格或换行结束 scanf同理
     //方法一:一个字符一个字符拿
 //     char ch = getchar();
 //     //char ch = cin.get();
 //     while(ch!='\n')
 //     {
 //         s+=ch;
 //         ch = getchar();
 //     }
     //方式二:
     getline(cin,s);
     
     size_t pos = s.rfind(' ');
     if(pos == string::npos)
     {
         cout <<s.size()<<endl;
     }
     else{
         cout << s.size() - pos-1;
     }
     return 0;    
 }

开区间取值

0 - 9 的下标是10个有效字符

所以算有效字符是size - 0 (size是最后一个字符的下一个)

如果用最后一个字符减0 则有效字符会少一个

最后修改:2025 年 06 月 21 日
如果觉得我的文章对你有用,请随意赞赏