【与C++的邂逅】--- string容器使用
Welcome to 9ilk'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[] (size_t pos);
const char& operator[] (size_t pos) const;
- 引用返回
int main()
{string s = "hello";for (size_t i = 0; i < s.size(); i++){cout << s.operator[](i) << " "; //显式[]+下标访问对象元素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[](size_t pos)
{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,它们都是定义在类域里的成员函数.
注 : C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型.
for (auto ch : s)cout << ch << endl;
C++11之后我们更喜欢用范围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* p1 |
const_iterator | 只读 迭代器指向数据不能写 | const int* p2 |
const 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的不同:vector<char>一次只能进一个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"); //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
注 :
1. 使用insert时,我们要注意传入区间和下标的合法性。
2. 一次insert的时间复杂度是O(N),一段程序中不断对string对象调用insert会有较大的时间消耗,因此在实践中我们要慎用。
3. 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
注 :
1. len表示的是从pos位置开始要替换的字符个数。
2. 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;
说明 :
1. 对于size和length都是求字符串的长度.length是主要针对串这个容器而设计的,而size是面对大多数容器设计的表示"容器元素个数"的接口.
2. 对于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('c');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
注意:
1. 严格来说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注意事项:
1. 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扩容是要多少扩多少;缩容是要缩多少就缩到多少。
注 :
1. 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
注 :
1. 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; //4
📌rfind
使用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]='*';found=str.find_first_of("aeiou",found+1);
}
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(x+y);
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.在C++98中,c_str()返回 const char* 类型,返回的字符串会以空字符结尾;在C++98中,data()返回 const char* 类型,返回的字符串不以空字符结尾
4. 在C++11版本中,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字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。故总共占16+4+4+4=28个字节。
- 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
完。
相关文章:

【与C++的邂逅】--- string容器使用
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: 与C的邂逅 本篇博客我们将来了解string容器本身以及接口的使用。 string是串,本质是一个字符数组,可以对其进行增删查改。 &am…...

1-18 平滑处理——高斯滤波 opencv树莓派4B 入门系列笔记
目录 一、提前准备 二、代码详解 cv2.GaussianBlur函数用于对图像进行高斯滤波。高斯滤波是一种平滑图像的技术,用于减少噪声和细节。函数的三个参数如下: 三、运行结果 四、完整工程贴出 一、提前准备 1、树莓派4B 及 64位系统 2、提前安装opencv库…...

小爱打工,你躺平!让「微信AI小助理」接管你的文件处理,一个字:爽!
前两天,搞了个微信 AI 小助理-小爱(AI),爸妈玩的不亦乐乎。 零风险!零费用!我把AI接入微信群,爸妈玩嗨了,附教程(下) 最近一直在迭代中,挖掘小爱的无限潜力: 链接丢给…...

管理学习(一)马云《赢在中国》创业演讲整理
目录 一、小公司也需要制度二、不要害怕冒险三、创业者要的不是技术,而是胆识四、不要惧怕和大企业竞争五、理念不一样,老板永远是对的六、要真实地为客户创造价值七、跟风险投资谈判,说到要做到八、风险投资,只能帮你不能救你九、…...

Opencv中的直方图(2)计算图像的直方图函数calcHist()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 计算一组数组的直方图。 函数 cv::calcHist 计算一个或多个数组的直方图。用于递增直方图bin的元组的元素是从相同位置的相应输入数组中获取的。…...

Buzzer:一款针对eBPF的安全检测与模糊测试工具
关于Buzzer Buzzer是一款功能强大的模糊测试工具链,该工具基于Go语言开发,可以帮助广大研究人员简单高效地开发针对eBPF的模糊测试策略。 功能介绍 下面给出的是当前版本的Buzzer整体架构: 元素解析: 1、ControlUnit:…...

若依框架登录鉴权详解(动态路由)
若依框架登录鉴权:1.获取token(过期在响应拦截器中实现),2.基于RBAC模型获取用户、角色和权限信息(在路由前置守卫),3.根据用户权限动态生成(从字符串->组件,根据permission添加动…...

孤儿进程、僵尸进程、守护进程(精灵进程)
目录 一、孤儿进程 二、僵尸进程 三、守护进程(精灵进程) 一、孤儿进程 定义:孤儿进程是指那些其父进程已经结束,但它们依然在运行的进程 创建一个孤儿进程: #include <stdio.h> #include <stdlib.h> #in…...

Centos9 网卡配置文件
1、Centos stream 9 网络介结 Centos以前版本,NetworkManage以ifcfg格式存储网络配置文件在/etc/sysconfig/networkscripts/目录中。但是,Centos steam 9现已弃用ifcfg格式,默认情况下,NetworkManage不再创建此格式的新配置文件。…...

ios免签H5
1、windows下载mobileconfig文件制作工具,可在csdn搜索iPhone_Mobileconfig_Tool下载安装;IOS 从APP Store 下载Apple Configurator 2 2、用申请的域名SSL证书给mobieconfig文件签名,最好下载Apache证书,里面包含 AE86211.crt…...

RedHat9.x-基本操作
本例以RedHat9.3为例 安装ifconfig命令 # dnf -y install net-tools 安装图形化 # dnf -y groupinstall "Server with GUI" 安装tmux # dnf -y install tmux -- screen已弃用 network-scripts 已弃用...

华为 HCIP-Datacom H12-821 题库 (5)
有需要题库的可以看主页置顶 需要题库的加Q裙 V群仅进行学习交流 1.以下关于堆叠 MAD 检测说法错误的是? A、堆系统互为代理进行 MAD 检测时,两个堆系统可以使用相同的D omain ID B、MAD 检测的方式分为直连检测、代理检测 C、MAD 代理检测要求所有堆叠…...

vue中oninput和@input区别
使用oninput绑定,此方法会导致中文输入法情况下,v-model和value不一致,原因是在中文输入法情况下vue中的v-model会自动return,v-mode绑定的值不变,value绑定的值可变。 :οninput"valuevalue.replace(/\D/g,)&qu…...

分布式锁(Redis的setnx、Redisson)
一、使用Redis的setnx实现分布式锁 1、使用Redis的setnx实现分布式锁出现的问题 (1) 宕机时的锁释放问题 在分布式系统中,如果一个节点获取了锁,但在执行任务过程中发生故障,没有释放锁,其他节点可能会一…...

从0开始深度学习(4)——线性回归概念
1 线性回归 回归(regression)指能为一个或多个自变量与因变量之间的关系进行建模。 1.1 线性模型 线性假设是指目标可以表示为特征的加权和,以房价和面积、房龄为例,可以有下面的式子: w称为权重(weigh…...

C语言中的预处理指令中的其中一对——#ifdef和#ifndef
目录 开头1.什么是#ifdef和#ifndef?2.#ifdef和#ifndef的实际应用判断ABCD这个宏是否被定义过判断HELLO这个宏是否没被定义过防止头文件重复定义 下一篇博客要说的东西 开头 大家好,我叫这是我58。今天,我们要学一下关于C语言中的预处理指令中的其中一对…...

交换机自动化备份配置(H3C_无人值守)
介绍: 在日常运维过程中,需要定时备份设备的配置,在设备数量过于庞大的情况下,对我们的运维工作会造成极大地不便,通过python自动化能够完美解决人工手动保存设备配置的问题。而且自动化运维在未来也一定是大势所趋&a…...

缓存预热有哪些方案?
一道经典面试题:缓存预热有哪些方案? 在系统业务高峰期到来之前,我们提前将一些热点数据加载到缓存中,进而提高系统的响应速度,这就是所谓的缓存预热。 那么怎么实现缓存预热呢? 一般来说,我…...

「iOS学习」——Masonry学习
iOS学习 前言Masonry的属性Masonry的使用基础APIAuto Boxing修饰语倍数中心点设置边距优先级使用 总结 前言 暑假我们学习了使用CocoaPods引入第三方库,实现使用SVG图片。而Masonry作为一个轻量级的布局架构,在使用中可以节省很多时间。故进行简单学习。…...

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署GitLab服务器
828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署Gitlab服务器 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、GitLab介绍2.1 GitLab简介2.2 GitLab主要特点 三、本次…...

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】
1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息; 2、DHT11采集病房温湿度信息&…...

计算机毕业设计 | SpringBoot+vue 游戏商城 steam网站管理系统(附源码)
1,项目背景 国家大力推进信息化建设的大背景下,城市网络基础设施和信息化应用水平得到了极大的提高和提高。特别是在经济发达的沿海地区,商业和服务业也比较发达,公众接受新事物的能力和消费水平也比较高。开展商贸流通产业的信息…...

【CH395的简单示例代码】
提供一个基于CH395的简单示例代码,这里将展示如何初始化CH395,并发送一个简单的HTTP请求。请注意,实际使用时还需要根据具体的硬件平台和开发环境调整代码。 假设我们使用的是一个具有SPI接口的微控制器,并且已经将CH395连接到该…...

AI模型:追求全能还是专精?
目录 引言 一、全能型AI模型的诱惑 1.1 通用智能的愿景 1.2 资源整合的优势 1.3 应对未知挑战的能力 1.4 挑战与不足 二、专精型AI模型的魅力 2.1 深度与精度的提升 2.2 成本控制与效率优化 2.3 易于监管与解释性增强 2.4 挑战与不足 三、全能型与专精型AI的全面评…...

ffmpeg音视频开发从入门到精通——ffmpeg 视频数据抽取
文章目录 FFmpeg视频处理工具使用总结环境配置主函数与参数处理打开输入文件获取流信息分配输出文件上下文猜测输出文件格式创建视频流并设置参数打开输出文件并写入头信息读取、转换并写入帧数据写入尾信息并释放资源运行程序注意事项源代码 FFmpeg视频处理工具使用总结 环境…...

Node.js之文件夹的操作
1.创建文件夹操作 // 导入fs模块 const fs require(fs)//创建文件夹操操作 fs.mkdir(./Page, err > {if (err) {console.log(操作失败)return}console.log(操作成功) }) 2.递归创建文件夹 //递归创建文件夹 fs.mkdir(./a/b/c, {recursive: true}, err > {if (err) {co…...

线程的四种操作
所属专栏:Java学习 1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链…...

自我指导:提升语言模型自我生成指令的能力
人工智能咨询培训老师叶梓 转载标明出处 传统的语言模型,尤其是经过指令微调的大型模型,虽然在零样本(zero-shot)任务泛化上表现出色,但它们高度依赖于人类编写的指令数据。这些数据往往数量有限、多样性不足…...

使用Node.js实现单文件上传功能—含代码解释
1、概念 文件上传的具体内容 在前端让用户发送(上传)图片,图片由后端(服务器)接收,并转存到到服务端设备上的操作node.js的文件上传功能主要是使用:multer 插件实现的 搭建一个图片上传的接口 先让接口开通,再去做插件下载/配置等…...

【机器人工具箱Robotics Toolbox开发笔记(一)】Matlab机器人工具箱简介
MATLAB是一款被广泛应用于科学计算和工程领域的专业软件。它的全称为Matrix Laboratory(矩阵实验室),因为其最基本的数据类型就是矢量与矩阵,所以在处理数学和科学问题时非常方便,可用于线性代数计算、图形和动态仿真的…...