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

python官方网站什么是淘宝搜索关键词

python官方网站,什么是淘宝搜索关键词,百科创建,西安专业网站开发联系电话在 C C C 中, s t r i n g string string 是标准库提供的一个类,用于处理字符串。它是基于模板的容器类,提供了许多成员函数和操作符,用于对字符串进行各种操作,比如插入、删除、查找、连接等。与 C C C 风格的字符串…

C C C++ 中, s t r i n g string string 是标准库提供的一个类,用于处理字符串。它是基于模板的容器类,提供了许多成员函数和操作符,用于对字符串进行各种操作,比如插入删除查找连接等。与 C C C 风格的字符串相比, s t r i n g string string 类更加安全和方便,它负责自动管理字符串内存,具有动态大小调整的能力。

文章目录

  • 前言 —— STL 简介
    • 1. 什么是 STL ?
    • 2. STL 的版本(了解)
    • 3. STL 的六大组件
  • 一、为什么学习 string 类?
    • 1. C语言中的字符串
    • 2. OJ 题
  • 二、如果学习 string 类?(查文档)
  • 三、补充 2 个 C++11 的知识点
    • 1. auto 关键字
    • 2. 范围 for
  • 四、迭代器(iterator)
  • 五、string 类的使用(常用接口)
    • 1. 常见构造
    • 2. 容量操作
    • 3. 访问及遍历操作
    • 4. 修改操作
    • 5. 非成员函数
  • 六、vs 和 g++ 下 string 结构的说明(了解)
    • 1. vs 下 string 的结构
    • 2. g++ 下 string 的结构
  • 七、string 类的拷贝问题
    • 1. 浅拷贝
    • 2. 深拷贝
    • 3. 写时拷贝(了解)
  • 八、string 类的模拟实现
  • 总结


前言 —— STL 简介

本篇博客作为 S T L STL STL 系列的开篇,首先要介绍一下什么是 S T L STL STL。不过要注意的是, s t r i n g string string 类不属于 S T L STL STL 的六大组件,而是在 S T L STL STL 出现之前就已经存在了。不过 s t r i n g string string 在实现和使用方式和 S T L STL STL 容器极为相似,因此把 s t r i n g string string 类暂且归为 S T L STL STL 部分学习。

1. 什么是 STL ?

S T L STL STL s t a n d a r d t e m p l a t e l i b a r a y standard\ template\ libaray standard template libaray - 标准模板库):是C++标准库重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

2. STL 的版本(了解)

  1. 原始版本

A l e x a n d e r S t e p a n o v 、 M e n g L e e Alexander Stepanov、Meng Lee AlexanderStepanovMengLee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 H P HP HP 版本 —— 所有 S T L STL STL 实现版本的始祖。

  1. P . J . P. J. P.J. 版本

P . J . P l a u g e r P.\ J.\ Plauger P. J. Plauger 开发,继承自 H P HP HP 版本,被 W i n d o w s V i s u a l C + + Windows\ Visual\ C++ Windows Visual C++ 采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。

  1. R W RW RW 版本

R o u g e W a g e Rouge\ Wage Rouge Wage 公司开发,继承自 H P HP HP 版本,被 C + + B u i l d e r C++\ Builder C++ Builder 采用,不能公开或修改,可读性一般。

  1. S G I SGI SGI 版本

S i l i c o n G r a p h i c s C o m p u t e r S y s t e m s Silicon\ Graphics\ Computer\ Systems Silicon Graphics Computer Systems I n c Inc Inc 公司开发,继承自 H P HP HP 版本。被 G C C ( L i n u x ) GCC(Linux) GCC(Linux) 采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面 S T L STL STL 要阅读部分源代码,主要参考的就是这个版本。

3. STL 的六大组件

在这里插入图片描述

可以看出, S T L STL STL 的主要内容是一个包罗数据结构与算法的软件框架。模板的出现大大提高了代码的复用性, S T L STL STL 使我们方便了非常多,不用再像 C C C 语言一样手搓算法和数据结构了。在算法竞赛中, S T L STL STL 也是一个常用且有力的工具。


一、为什么学习 string 类?

1. C语言中的字符串

C C C 语言中,字符串是以 ‘ \ 0 ’ ‘\backslash 0’ ‘\0’ 结尾的一些字符的集合。为了操作方便, C C C 标准库中提供了一些 s t r str str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合 O O P OOP OOP O b j e c t O r i e n t e d P r o g r a m m i n g Object\ Oriented\ Programming Object Oriented Programming)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问

因此,在 C++ 中,采用面向对象的思想,把 s t r i n g string string 这个常用的字符串封装成一个 s t r i n g string string ,这样我们用户想使用 s t r i n g string string 这个字符串类型的时候,只需要调用其接口即可。

2. OJ 题

O J OJ OJ 中,有关字符串的题目基本以 s t r i n g string string 类的形式出现。而且在常规工作中,为了简单、方便、快捷,基本都使用 s t r i n g string string 类,很少有人去使用 C C C 库中的字符串操作函数。

以下面一道面试题为例:

【题目信息】 L e e t C o d e 415. LeetCode\ 415. LeetCode 415. 字符串相加

在这里插入图片描述

【题目解析】

这道题是高精度加法,当我们遇到很大的两个数(即使开 l o n g l o n g long\ long long long 也存不下)做加法运算的时候,我们就要用字符串来存下数字的每一位,然后模拟竖式运算,通过各位相加、进位加 1 1 1 等操作,来实现大整数的加法。题目的具体做法思路如下:

  1. 字符串转化为数字数组:

首先,我们知道两个高精度整数是以字符串的形式存储的,因此为了方便逐位相加,我们需要将字符串转换成倒序的数组,使得个位对齐,这样可以从低位向高位依次相加。

  1. 逐位相加并处理进位:

对于每一位数字,两个数组中的对应位相加,记录进位。进位部分会加到下一位的相加过程中。如果当前位的和大于等于 10 10 10,则需要将该位的值取模 10 10 10,余数保留,进位值加到下一位。

  1. 处理进位后的结果:

在完成所有位的相加后,如果最高位有进位,则需要在结果中保留该位。否则,最高位是 0 0 0 时则忽略。

  1. 将结果转回字符串输出:

最后,将结果数组从高位到低位依次转换为字符串,并输出最终结果。

【代码示例】

class Solution {
public:string addStrings(string a, string b) {int x[10000] = {0}, y[10000] = {0}, z[10000] = {0};// 将字符串 a, b 转换为倒序数组for (int i = 0; i < a.size(); i++) x[a.size() - 1 - i] = a[i] - '0';for (int i = 0; i < b.size(); i++) y[b.size() - 1 - i] = b[i] - '0';// 求和并处理进位int len = max(a.size(), b.size());for (int i = 0; i < len; i++) {z[i] += x[i] + y[i];  // 当前位相加z[i + 1] += z[i] / 10;  // 处理进位z[i] %= 10;  // 保留个位}// 检查最高位是否有进位if (z[len] != 0) len++;// 构造返回结果字符串string ans;for (int i = len - 1; i >= 0; i--)ans.push_back(z[i] + '0');return ans;}
};

二、如果学习 string 类?(查文档)

不只是 s t r i n g string string 类,我们在学习 S T L STL STL 的时候一定要勤于查 C++ 官方文档 s t r i n g string string 类的文档介绍

在这里插入图片描述

注意:在使用 s t r i n g string string 类时,必须包含 # i n c l u d e include include < s t r i n g string string > 头文件以及 u s i n g n a m e s p a c e s t d ; using\ namespace\ std; using namespace std;


三、补充 2 个 C++11 的知识点

1. auto 关键字

C++11 中, a u t o auto auto 是一个类型指示符来指示编译器, a u t o auto auto 声明的变量必须由编译器在编译时期推导而得。(自动识别类型注意:只有C++11以上的版本才支持auto。

实际上, a u t o auto auto 最大的用途是为了简化代码:(但是一定程度上牺牲了可读性)

string s;string::iterator it = s.begin();//像迭代器这种长类型可以直接用auto代替,来简化代码
auto it = s.begin();

除此之外,还有一些注意事项:

  1. a u t o auto auto 声明指针类型时,用 a u t o auto auto a u t o ∗ auto^* auto 没有任何区别(自动识别类型),但用 a u t o auto auto 声明引用类型时则必须加 & \& & & \& & 代表可修改)。
auto x = 1;		//intauto y = &x;	//int*
auto* z = &x;	//int*cout << typeid(x).name() << endl;	//输出x的类型
cout << typeid(y).name() << endl;	//输出y的类型
cout << typeid(z).name() << endl;	//输出z的类型//int
auto m = x;			
m = 2;
cout << x << endl;//int&
auto& n = x;		
n = 2;
cout << x << endl;

运行结果为:

在这里插入图片描述

  1. 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
auto a = 1, b = 2;//error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto c = 3, d = 4.0;
  1. a u t o auto auto 不能作为函数的参数,可以做返回值,但是建议谨慎使用。
auto add(int a, int b)
{return a + b;
}//error C3533: 参数不能为包含“auto”的类型
int sub(auto b, int c)
{return b - c;
}int main()
{add(2, 1);sub(4, 3);
}
  1. a u t o auto auto 不能直接用来声明数组
//error C3318 : “auto[]” : 数组不能具有其中包含“auto”的元素类型
auto a[] = { 1,2,3 };

2. 范围 for

C++11 中,引入了基于范围的 f o r for for 循环。 f o r for for 循环后的括号由冒号 : : : 分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。(范围 f o r for for 的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到)

范围 f o r for for 可以作用到数组和容器对象上进行遍历:

int a[] = { 1,2,3,4,5 };//C++98的遍历
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{cout << a[i] << " ";
}
cout << endl;//C++11的遍历//a不可修改(传值)
for (auto i : a)
{cout << i << " ";
}
cout << endl;//加上&代表可修改(传引用)
for (auto& i : a)
{i *= 2;cout << i << " ";
}
cout << endl;

运行结果为:

在这里插入图片描述


四、迭代器(iterator)

  1. i t e r a t o r iterator iterator
string s("hello world");for (string::iterator it = s.begin(); it != s.end(); it++)
{cout << *it;	//hello world
}
  1. r e v e r s e _ i t e r a t o r reverse\_iterator reverse_iterator
string s("hello world");for (string::reverse_iterator it = s.rbegin(); it != s.rend(); ++it)
{cout << *it;	//dlrow olleh
}
  1. c o n s t _ i t e r a t o r const\_iterator const_iterator
const string s = "hello world";for (string::const_iterator it = s.cbegin(); it != s.cend(); it++)
{//error C3892: “it”: 不能给常量赋值//*it += 1;cout << *it;	//hello world
}
  1. c o n s t _ r e v e r s e _ i t e r a t o r const\_reverse\_iterator const_reverse_iterator
const string s = "hello world";for (string::const_reverse_iterator it = s.crbegin(); it != s.crend(); it++)
{//error C3892: “it”: 不能给常量赋值//*it += 1;cout << *it;	//dlrow olleh
}

五、string 类的使用(常用接口)

下面只列举了 s t r i n g string string 最常用的几个接口,更多详细信息可以自行查官方文档: s t r i n g string string

1. 常见构造

构造( c o n s t r u c t o r constructor constructor)函数:

函数名称功能说明
s t r i n g ( ) string() string()重点构造空的 s t r i n g string string 类对象,即空字符串
s t r i n g ( c o n s t c h a r ∗ s ) string(const\ char^*\ s) string(const char s)重点 C − s t r i n g C-string Cstring (字符数组)来构造 s t r i n g string string 类对象
s t r i n g ( s i z e _ t n , c h a r c ) string(size\_t\ n,\ char\ c) string(size_t n, char c) s t r i n g string string 类对象中包含 n n n 个字符 c c c
s t r i n g ( c o n s t s t r i n g & s ) string(const\ string\&\ s) string(const string& s)重点拷贝构造函数
void Member_functions()
{string s1; 				//1.构造空的string类对象s1string s2("hello bit"); //2.用C格式字符串构造string类对象s2string s3(s2); 			//3.拷贝构造s3
}

2. 容量操作

函数名称功能说明
s i z e size size重点返回字符串有效字符长度
l e n g t h length length重点返回字符串有效字符长度
c a p a c i t y capacity capacity返回空间总大小
e m p t y empty empty重点检测字符串是否为空串,是返回 t r u e true true,否则返回 f a l s e false false
c l e a r clear clear重点清空有效字符
r e s e r v e reserve reserve重点为字符串预留空间
r e s i z e resize resize重点将有效字符的个数改成 n n n 个,多出的空间用字符 c c c 填充
// 测试string容量相关的接口
// size/clear/resize
void Capacity1()
{string s("hello, ybc!!!");cout << "s.size():    " << s.size() << endl;cout << "s.length():  " << s.length() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << "s:           " << s << endl;cout << endl;// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小s.clear();cout << "s.size():    " << s.size() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << "s:           " << s << endl;cout << endl;// 将s中有效字符个数增加到10个,多出位置用'a'进行填充// “aaaaaaaaaa”s.resize(10, 'a');cout << "s.size():    " << s.size() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << "s:           " << s << endl;cout << endl;// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充// "aaaaaaaaaa\0\0\0\0\0"// 注意此时s中有效字符个数已经增加到15个s.resize(15);cout << "s.size():    " << s.size() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << "s:           " << s << endl;cout << endl;// 将s中有效字符个数缩小到5个s.resize(5);cout << "s.size():    " << s.size() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << "s:           " << s << endl;cout << endl;
}

在这里插入图片描述

void Capacity2()
{string s;// 测试reserve是否会改变string中有效元素个数s.reserve(100);cout << "s.size():    " << s.size() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << endl;// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小s.reserve(50);cout << "s.size():    " << s.size() << endl;cout << "s.capacity():" << s.capacity() << endl;cout << endl;
}

在这里插入图片描述

如果 s t r i n g string string 为空,那么我们当我们插入数据的时候, s t r i n g string string 会不断扩容:

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';}}
}

在这里插入图片描述

构建 s t r i n g string string 时,如果提前已经知道 s t r i n g string string 中大概要放多少个元素,可以提前将 s t r i n g string string 中空间设置好:

void TestPushBackReserve()
{string s;s.reserve(100);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';}}
}

在这里插入图片描述

因此,利用 r e s e r v e reserve reserve 提高插入数据的效率,可以避免增容带来的开销。

总结:

  1. s i z e ( ) size() size() l e n g t h ( ) length() length() 方法底层实现原理完全相同,引入 s i z e ( ) size() size() 的原因是为了与其他容器的接口保持一致,一般情况下基本都是用 s i z e ( ) size() size()

  2. c l e a r ( ) clear() clear() 只是将 s t r i n g string string有效字符清空,不改变底层空间大小 c a p a c i t y capacity capacity)。

  3. r e s i z e ( s i z e _ t n ) resize(size\_t\ n) resize(size_t n) r e s i z e ( s i z e _ t n , c h a r c ) resize(size\_t\ n, char\ c) resize(size_t n,char c) 都是将字符串中有效字符个数改变到 n n n 个。
    不同的是:当字符个数增多时 r e s i z e ( n ) resize(n) resize(n) 0 0 0 来填充多出的元素空间, r e s i z e ( s i z e _ t n , c h a r c ) resize(size\_t\ n, char\ c) resize(size_t n,char c) 用字符 c c c 来填充多出的元素空间。

注意: r e s i z e resize resize 在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小(扩容);如果是将元素个数减少,底层空间总大小不变(不缩)。

  1. r e s e r v e ( s i z e _ t r e s _ a r g = 0 ) reserve(size\_t\ res\_arg=0) reserve(size_t res_arg=0):为 s t r i n g string string 预留空间不改变有效元素个数,当 r e s e r v e reserve reserve 的参数小于 s t r i n g string string 的底层空间总大小时, r e s e r v e r reserver reserver 不会改变容量大小。

3. 访问及遍历操作

函数名称功能说明
o p e r a t o r [ ] operator[\ ] operator[ ]重点返回 p o s pos pos 位置的字符, c o n s t s t r i n g const\ string const string 类对象调用
b e g i n + e n d begin+ end begin+end b e g i n begin begin 获取第一个字符的迭代器 + + + e n d end end 获取最后一个字符下一个位置的迭代器
r b e g i n + r e n d rbegin + rend rbegin+rend r b e g i n rbegin rbegin 获取最后一个字符的迭代器 + + + r e n d rend rend 获取第一个字符下一个位置的迭代器
范围 f o r for for C C C++ 11 11 11 支持更简洁的范围 f o r for for 的新遍历方式

s t r i n g string string 的遍历一般是:

  1. b e g i n ( ) + e n d ( ) begin()+end() begin()+end()

  2. f o r + o p e r a t o r [ ] for+operator[\ ] for+operator[ ] / / / 范围 f o r for for

注意: s t r i n g string string 遍历时使用最多的还是 f o r for for + + + 下标,或者范围 f o r for for C C C ++ 11 11 11 后才支持); b e g i n ( ) + e n d ( ) begin()+end() begin()+end() 大多数使用在需要使用 S T L STL STL 提供的算法操作 s t r i n g string string 时,比如:采用 r e v e r s e reverse reverse 逆置 s t r i n g string string

1. 访问操作:

void Element_access1()
{string s1("hello ybc");const string s2("Hello ybc");cout << s1 << " " << s2 << endl;cout << s1[0] << " " << s2[0] << endl;s1[0] = 'H';cout << s1 << endl;// s2[0] = 'h';   代码编译失败,因为const类型对象不能修改
}

在这里插入图片描述

2. 遍历操作:

void Element_access2()
{string s("hello ybc");// 1. for + operator[]for (auto i = 0; i < s.size(); ++i)cout << s[i];cout << endl << endl;// 2.迭代器string::iterator it = s.begin();while (it != s.end()){cout << *it;++it;}cout << endl << endl;// string::reverse_iterator rit = s.rbegin();// C++11之后,直接使用auto定义迭代器,让编译器推导迭代器的类型auto rit = s.rbegin();while (rit != s.rend()){cout << *rit;++rit;}cout << endl << endl;// 3.范围forfor (auto ch : s)cout << ch;cout << endl << endl;
}

在这里插入图片描述

4. 修改操作

函数名称功能说明
p u s h _ b a c k push\_back push_back在字符串后尾插字符 c c c
a p p e n d append append在字符串后追加一个字符串
o p e r a t o r + = operator\ += operator +=重点在字符串后追加字符串 s t r str str
c _ s t r c\_str c_str重点返回 C C C 格式字符串
f i n d + n p o s find + npos find+npos重点从字符串 p o s pos pos 位置开始往后找字符 c c c,返回该字符在字符串中的位置
r f i n d + n p o s rfind + npos rfind+npos重点从字符串 p o s pos pos 位置开始往前找字符 c c c,返回该字符在字符串中的位置
s u b s t r substr substr重点 s t r str str 中从 p o s pos pos 位置开始,截取 n n n 个字符,然后将其返回
i n s e r t insert insert重点从字符串 p o s pos pos 位置开始,插入 n n n 个字符 c c c / / / 字符串 s t r i n g string string
e r a s e erase erase重点从字符串 p o s pos pos 位置开始,删除长度为 n n n 的字符串
r e p l a c e replace replace重点从字符串 p o s pos pos 位置开始,将 n n n 个字符替换成字符串 s t r i n g string string

注意: n p o s npos npos s t r i n g string string 里面的一个静态成员变量:static const size_t npos = -1;

测试 s t r i n g string string

  1. 插入(拼接)方式: p u s h _ b a c k / a p p e n d / o p e r a t o r + = / i n s e r t push\_back / append / operator+= / insert push_back/append/operator+=/insert

  2. 正向和反向查找: f i n d ( ) + r f i n d ( ) find() + rfind() find()+rfind()

  3. 截取子串: s u b s t r ( ) substr() substr()

  4. 删除: e r a s e erase erase

void Modifiers()
{string str;str.push_back(' ');   // 在str后插入空格str.append("hello "); // 在str后追加一个字符"hello "str += 'y';           // 在str后追加一个字符'y'   str += "bc";          // 在str后追加一个字符串"bc"cout << str << endl;		   // 以string方式打印字符串cout << str.c_str() << endl;   // 以C语言的方式打印字符串
}

在这里插入图片描述

【应用1】获取 f i l e file file 的后缀

string file("string.cpp");//1.从file中找到.的下标pos
size_t pos = file.rfind('.');
//2.从pos开始,截取pos及pos后面的字符子串
string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;

在这里插入图片描述

【应用2】取出 u r l url url 中的域名

string url("http://www.cplusplus.com/reference/string/string/find/");
cout << url << endl;//找到域名的协议前缀
size_t start = url.find("://");
//如果找不到协议前缀(说明不是协议)
if (start == string::npos)
{cout << "invalid url" << endl;return 0;
}
//找到域名的起始位置
start += 3;
//找到域名的终止位置
size_t finish = url.find('/', start);
//获取域名子串
string address = url.substr(start, finish - start);cout << address << endl;

在这里插入图片描述

【应用3】删除 u r l url url 的协议前缀

string url("http://www.cplusplus.com/reference/string/string/find/");//找到域名的协议前缀
size_t pos = url.find("://");
//删除协议前缀
url.erase(0, pos + 3);cout << url << endl;

在这里插入图片描述

string s = "o world ";//头插
s.insert(0, "hell");
//尾插10个字符a
s.insert(s.size(), 10, 'a');、cout << s << endl;//删除空格后的所有元素(到npos结束)
auto pos = s.find(' ');
s.erase(pos + 1);cout << s << endl;//把hello替换成nihao
s.replace(0, s.size(), "nihao");cout << s << endl;

在这里插入图片描述

总结:

  1. s t r i n g string string 尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c' 三种的实现方式差不多,一般情况下 s t r i n g string string 类的 += 操作用的比较多,+= 操作不仅可以连接单个字符,还可以连接字符串

  2. s t r i n g string string 操作时,如果能够大概预估到放多少字符,可以先通过 r e s e r v e reserve reserve 把空间预留好。

5. 非成员函数

这里主要介绍的是 s t r i n g string string 类的运算符重载:

函数名称功能说明
o p e r a t o r + operator\ + operator +尽量少用,因为传值返回,导致深拷贝效率低
o p e r a t o r > > operator\ >> operator >>重点输入运算符重载
o p e r a t o r < < operator\ << operator <<重点输出运算符重载
g e t l i n e getline getline重点获取一行字符串,遇到 d e l i m delim delim 停止读入(默认是 ’ \0 ’ )
r e l a t i o n a l o p e r a t o r s relational\ operators relational operators重点大小比较

在这里插入图片描述

g e t l i n e getline getline 这个函数可以获取一整行字符串,而 c i n cin cin 输入的字符串自动到空格结束
因此, g e t l i n e getline getline 的也是非常有用的,要注意其第一个参数为输入流

void Non_member_function_overloads()
{// 注意:string类对象支持直接用cin和cout进行输入和输出string s1, s2;getline(cin, s1);getline(cin, s2);cout << "s1:" << s1 << endl << "s2:" << s2 << endl;if (s1 < s2) cout << "s1 < s2" << endl;else if (s1 > s2) cout << "s1 > s2" << endl;else if (s1 == s2) cout << "s1 == s2" << endl;string s3 = s1 + " " + s2; cout << "s3:" << s3 << endl;
}

在这里插入图片描述

注意:这里字符串比较大小是按照字典序比较的(不是按照长度比较)。


六、vs 和 g++ 下 string 结构的说明(了解)

注意:下述结构是在 32 32 32 位平台下进行验证, 32 32 32 位平台下指针占 4 4 4 个字节。

1. vs 下 string 的结构

s t r i n g string string 总共占 28 28 28 个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 s t r i n g string string 中字符串的存储空间:

  1. 当字符串长度小于 16 16 16,使用内部固定的字符数组 B u f Buf Buf)来存放。
  2. 当字符串长度大于等于 16 16 16,从堆上开辟空间(动态申请空间)。
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的:

1. 首先:大多数情况下字符串的长度都小于 16 16 16,那 s t r i n g string string 对象创建好之后,内部已经有了 16 16 16 个字符数组的固定空间,不需要通过堆创建,效率高。

2. 其次:还有一个 s i z e _ t size\_t size_t 字段保存字符串长度,一个 s i z e _ t size\_t size_t 字段保存从堆上开辟空间总的容量

3. 最后:还有一个指针做一些其他事情。

4. 故总共占 16 + 4 + 4 + 4 = 28 16+4+4+4=28 16+4+4+4=28 个字节。

在这里插入图片描述

2. g++ 下 string 的结构

G G G++下, s t r i n g string string 是通过写时拷贝实现的, s t r i n g string string 对象总共占 4 4 4 个字节,内部只包含了一个指针,该指针将来指向一块空间,内部包含了如下字段:

  1. 空间总大小 l e n g t h length length
  2. 字符串有效长度 c a p a c i t y capacity capacity
  3. 引用计数 r e f c o u n t refcount refcount
  4. 指向堆空间的指针,用来存储字符串。
struct _Rep_base
{size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount;
};

七、string 类的拷贝问题

1. 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

在这里插入图片描述

2. 深拷贝

深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。如果一个类中涉及到资源的管理,其拷贝构造函数赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

1. 传统写法:

//拷贝构造
string(const string& s)
{_str = new char[s._size + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}//赋值重载
string& operator = (const string& s)
{if (this != &s){delete[] _str;_str = new char[s._size + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;
}

2. 现代写法:

void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}//拷贝构造
string(const string& s)
{string tmp(s._str);	//调用构造函数swap(tmp);
}//赋值重载
string& operator = (string tmp)	//直接传拷贝
{swap(tmp);return *this;
}

在这里插入图片描述

3. 写时拷贝(了解)

写时拷贝:在浅拷贝的基础之上增加了引用计数的方式来实现的。(一种拖延症)

由于深拷贝需要开空间,消耗大;而浅拷贝会有以下两个问题(以两个对象指向同一块空间为例):

  1. 同一块空间会被析构两次

  2. 一个修改会影响另一个

因此,引入了引用计数来优化深浅拷贝:

引用计数:用来记录资源使用者的个数。(有几个对象指向这个资源)

在构造时,将资源的计数给成 1 1 1,每增加一个对象使用该资源,就给计数增加 1 1 1,当某个对象被销毁时,先给该计数减 1 1 1,然后再检查是否需要释放资源,如果计数为 1 1 1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

总结:写时拷贝使其只有在被用到的时候才会进行深拷贝,避免了许多不必要的消耗。(博弈:拷贝后,如果不写就赚了)


八、string 类的模拟实现

对于 C C C++ 官方文档中,对于 s t r i n g string string 的实现是对 b a s i c _ s t r i n g basic\_string basic_string 的重命名,主要是用来处理 U T F − 8 UTF-8 UTF8 c h a r char char 类型数据的。这里模拟实现的是 s t r i n g string string 类( c h a r char char 类型顺序表)的大部分基本功能,依然采用声明与定义分离的形式,最后进行测试(一些短小且频繁调用的函数不用分离,相当于设置成内联函数)。

总共分为三个文件: s t r i n g . h string.h string.h 用来存放 s t r i n g string string 类的声明; s t r i n g . c p p string.cpp string.cpp 用来定义声明的函数; t e s t . c p p test.cpp test.cpp 用来测试。

注意:这里每个文件中的 s t r i n g string string 都是自己定义的,为了和 s t d : : s t r i n g std::string std::string 作区分,我们要单独创建一个命名空间,这里我用的是 n a m e s p a c e y b c namespace\ ybc namespace ybc

  1. s t r i n g . h string.h string.h
#define _CRT_SECURE_NO_WARNINGS #pragma once#include<iostream>#include<assert.h>#include<string>using namespace std;namespace ybc
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin() const{return _str;}iterator end() const{return _str + _size;}const_iterator cbegin() const{return _str;}const_iterator cend() const{return _str + _size;}//短小且频繁调用的函数,直接定义到类里面,默认为内联inline/*string():_str(new char[1] {'\0'}),_size(0),_capacity(0){}*/string(const char* str = ""){_size = strlen(str);_str = new char[strlen(str) + 1];_capacity = _size;	//capacity不包含'\0'strcpy(_str, str);}/*string(const string& s){_str = new char[s._size + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}*/void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string(const string& s){string tmp(s._str);	//调用构造函数swap(tmp);}/*string& operator = (const string& s){if (this != &s){delete[] _str;_str = new char[s._size + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}*/string& operator = (string tmp){swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}const char* c_str() const{return _str;}void clear(){_str[0] = '\0';_size = 0;}size_t size() const{return _size;}size_t length() const{return _size;}size_t capacity() const{return _size;}bool empty() const{return _size == 0;}		char& operator [] (size_t pos){assert(pos < _size);return _str[pos];}const char& operator [] (size_t pos) const{assert(pos < _size);return _str[pos];}//声明和定义分离void reserve(size_t n);void push_back(char c);void append(const char* s);string& operator += (char c);string& operator += (const char* s);void insert(size_t pos, char c);void insert(size_t pos, const char* s);void erase(size_t pos, size_t len = npos);size_t find(char c, size_t pos = 0);size_t find(const char* s, size_t pos = 0);string substr(size_t pos = 0, size_t len = npos);private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;static const size_t npos;};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);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& in, string& s);istream& getline(istream& in, string& s);
}
  1. s t r i n g . c p p string.cpp string.cpp
#include"string.h"namespace ybc
{const size_t string::npos = -1;void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = c;_str[_size] = '\0';
}void string::append(const char* s){size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, s);_size += len;}string& string::operator += (char c){push_back(c);return *this;}string& string::operator += (const char* s){append(s);return *this;}void string::insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//挪动数据size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;}void string::insert(size_t pos, const char* s){assert(pos <= _size);size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}for (size_t i = 0; i < len; i++){_str[pos + i] = s[i];}_size += len;}void string::erase(size_t pos, size_t len){assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos + len; i < _size; i++){_str[i - len] = _str[i];}_size -= len;}}size_t string::find(char c, size_t pos){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;}size_t string::find(const char* s, size_t pos){assert(pos < _size);const char* ptr = strstr(_str + pos, s);if (ptr == nullptr){return npos;}else{return ptr - _str;}}string string::substr(size_t pos, size_t len){assert(pos < _size);//更新一下lenif (len > _size - pos){len = _size - pos;}string sub;reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}bool operator <	(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator == (const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 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);}bool operator != (const string& s1, const string& s2){return !(s1 == s2);}ostream& operator << (ostream& out, const string& s){for (auto c : s){out << c;}return out;}istream& operator >> (istream& in, string& s){s.clear();//避免多次扩容的优化const int N = 1024;char buff[N];int i = 0;char c; in.get(c);while (c != ' ' && c != '\n'){buff[i++] = c;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}in.get(c);}if (i > 0){buff[i] = '\0';s += buff;}return in;}istream& getline(istream& in, string& s){s.clear();//避免多次扩容的优化const int N = 1024;char buff[N];int i = 0;char c; in.get(c);while (c != '\n'){buff[i++] = c;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}in.get(c);}if (i > 0){buff[i] = '\0';s += buff;}return in;}
}
  1. t e s t . c p p test.cpp test.cpp
#include"string.h"namespace ybc
{void test1(){string s("hello world");cout << s.c_str() << endl;for (int i = 0; i < s.size(); i++){s[i] += 2;}cout << s.c_str() << endl;for (auto& i : s){i -= 2;cout << i;}cout << endl;for (string::iterator it = s.begin(); it != s.end(); ++it){cout << *it;}cout << endl;}void test2(){string s("hello ");s.append("ybc ");s += "nb plus !";s.insert(0, "--------");s += "--------";s.erase(0, 5);s.erase(s.size() - 5);cout << s.c_str() << " " << s.find(" ", 5) << endl;}void test3(){string s = "hello world";string ss = s.substr(s.find("w"));cout << ss.c_str() << endl;//析构2次string copy(s);cout << copy.c_str() << endl;string sss;sss = s;cout << sss.c_str() << endl;}void test4(){string s1 = "hello", s2 = "world";cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;if (s1 < s2) cout << "s1 < s2" << endl;if (s1 > s2) cout << "s1 > s2" << endl;if (s1 <= s2) cout << "s1 <= s2" << endl;if (s1 >= s2) cout << "s1 >= s2" << endl;if (s1 == s2) cout << "s1 == s2" << endl;if (s1 != s2) cout << "s1 != s2" << endl;}void test5(){string s = "hello world";cin >> s;getline(cin, s);cout << s << endl;}void test6(){string s = "hello world";string ss = s;string sss;sss = ss;cout << s << endl << ss << endl << sss << endl;}void test7(){string s = "hello world";string ss = "xxxxxxxxxxxxxxxxxxxxxxx";std::swap(s, ss);	//C++98:3次深拷贝(不推荐)cout << s << endl << ss << endl;s.swap(ss);cout << s << endl << ss << endl;}
}int main()
{//ybc::test1();//ybc::test2();//ybc::test3();//ybc::test4();//ybc::test5();//ybc::test6();ybc::test7();return 0;
}

总结

以上就是对 C C C++ 标准库 S T L STL STL 的引入,以及介绍了 C C C++ 标准库中 s t r i n g string string 类的基本使用以及一些 C C C++11 的新特性,为后续学习正式的 S T L STL STL 容器做好铺垫。最后,将 s t r i n g string string 类的常用功能进行了模拟实现,对 s t r i n g string string 类的使用和底层有了更深刻的认识。

http://www.dtcms.com/wzjs/49677.html

相关文章:

  • 承德网站建设报价太原今日头条
  • 网站建设推广优化网站怎么被百度收录
  • 医疗网站爱站查询
  • 北碚网站建设武汉百度推广电话
  • 怎样做免费网站郑州好的seo外包公司
  • 网站优化内链怎么做拉新推广渠道
  • 关于做公司app的软件或网站网站seo搜索
  • 网站运营周期如何建立自己的网页
  • 做网站的代码seo按照搜索引擎的什么对网站
  • 一个人搞得定网站建设站长工具网站
  • 高端网站建设的品牌百度seo怎么查排名
  • 手机网站wap免费搭建自己的网站
  • 花生壳 做网站快速网站推广公司
  • 直播课网站怎样做的石家庄网站seo
  • 网站设计能出来什么微信推广多少钱一次
  • 企业网站建设制作设计哪家最专业小说引流推广
  • 万盛网站建设公司网站备案查询
  • 做公务员题哪个网站比较好阿里指数官网
  • 象山seo的优化佛山网站优化软件
  • 陕西 网站备案优化网站seo方案
  • 互联网站是不是自媒体平台代引流推广公司
  • 软件开发培训学校哪家比较好深圳关键词seo
  • 网站建设前期情况说明网站源码建站
  • 优化方案生物必修一北京优化seo排名优化
  • 个人做网站的必要性网站推广营销
  • 一个专业做设计的网站shodan搜索引擎
  • 蔷薇花园网站怎么做的网络营销的概述
  • 广州优质网站排名公司做任务赚佣金一单10块
  • 在线设计装修的网站河南郑州网站推广优化
  • wordpress中国网站排名手机百度引擎搜索入口