16. C++标准库


C++标准库兼容C语言标准函数库,可以在C++标准库中直接使用C语言标准函数库文件,同时C++标准库增加了自己的源代码文件,新增文件使用C++编写,多数代码放在std命名空间中,所以连接C++标准库文件后还需要 using namespace std;。


【终端输入输出】

C++标准库iostream文件提供终端输入输出功能,此文件同时兼容C语言标准函数库中的终端输入输出函数。

终端输出

cout对象用于终端输出,使用方式如下:

cout << 内容1 << 内容2 << 内容3 ......;

每个输出内容使用 << 符号连接,输出内容数量可变,char类型数据不会转换,其它数学数据会自动转换为字符串表示形式,若要输出换行则添加 << endl。

#include <iostream>
using namespace std;
int main()
{char ali[] = "阿狸";int age = 8;cout << "姓名:" << ali  << endl << "年龄:" << age << "岁" << endl;return 0;
}

使用cout输出数学数据时,前缀0表示8进制,前缀0x表示16进制。

#include <iostream>
using namespace std;
int main()
{/* 输出20、16、32,多个输出内容可以换行存储 */cout << 20 << endl<< 020 << endl<< 0x20 << endl;return 0;
}

终端输入

cin对象用于终端输入,使用方式类似cout,输入符号为 >>。

#include <iostream>
using namespace std;
int main()
{int a,b;cout << "请输入两个整数" << endl;cin >> a >> b;cout << "两个数据相加结果为:" << a+b << endl;return 0;
}


【STL】

C++标准库最大的特点是增加了数据结构相关类,这组类称为标准模板库,英文简称STL,内部代码大量使用了虚拟类型、符号函数。

数组

array 数组

array内部定义了一个私有数组和一些操作数组函数,常用成员函数如下:

at,使用下标调用数组元素,at会进行数组越界访问检查,越界访问会引发异常并退出程序。
size,返回数组元素个数。
data,返回数组第一个元素的指针。
swap,与另一个array数组进行元素交换,两个数组的元素都将改变。
fill,为所有元素设置一个初始值。

另外array有很多符号成员函数,可以使用[]符号访问内部元素([]符号函数不进行数组越界访问检查),使用=符号在两个长度相同的array对象之间赋值。

#include <iostream>
#include <array>       //需要连接array文件
using namespace std;
int main()
{array<int, 5> a = {1,2,3,4,5};    //定义int类型数组,包含5个元素for(int i = 0; i < a.size(); i++){printf("%d\n", a[i]);}return 0;
}


array对象可以嵌套定义,从而定义多维数组:

array<array<int, 2>, 3> a = {{{1, 2}, {3, 4}, {5, 6}}};

赋值时需要使用三层大括号,最外层{}表示为对象a赋值,中层{}表示为内部二维数组赋值,内层{}表示为一维数组赋值。

valarray 数组

valarray数组新增了很多了处理数学数据的成员函数,同时array有的函数valarray基本也有。

sum,返回所有元素相加后的值。
max,返回值最大的元素。
min,返回值最小的元素。

常用符号函数如下:

operator+,为数组中每个元素加上指定数值,增加的数值可以使用另一个valarray指定,此时将同位置的元素相加。
operator-,同上,减去指定数值。
operator*,同上,乘以指定数值。
operator/,同上,除以指定数值。

另外与array的使用方式有一些区别:
1.valarray对象使用构造函数指定数组元素个数,或者创建对象时直接为数组赋值,根据赋值数据个数确定元素数量。
2.创建对象未赋值时,所有元素设置初始值为0,而array不会设置元素初始值。

#include <iostream>
#include <valarray>
using namespace std;
int main()
{valarray<int> a = {1,2,3,4,5};valarray<int> b = {6,7,8,9,10};a = a + b;for(int i = 0; i < a.size(); i++){printf("%d\n", a[i]);}return 0;
}

动态数组

动态数组是长度可变的数组,通过new申请内存实现长度可变,vector类提供动态数组功能,常用成员函数如下:

at,使用下标调用数组元素,同时进行越界访问检查。
size,返回数组元素个数。
data,返回数组第一个元素的指针。
swap,与另一个vector对象进行元素交换。
empty,判断数组是否为空并返回布尔值,为空返回1,否则返回0。
push_back,在末尾添加一个元素。
pop_back,在末尾删除一个元素。
front,返回数组第一个元素。
back,返回数组最后一个元素。
end,提取数组末尾元素的指针,并将指针+1再返回,可以与insert函数配合使用。
insert,在数组指定位置插入数据(位置使用指针指定),此位置及之后的数据向后移动,此函数为重载函数。
erase,删除数组指定位置的数据(位置使用指针指定),此位置之后的数据向前移动,此函数为重载函数。
clear,清空内部元素。


定义方式:

vector<int> a;                //定义int类型动态数组。
vector<int> a(5);             //使用构造函数指定数组元素个数,元素初始值为0。
vector<int> a(5, 1);          //使用构造函数指定数组元素个数、元素初始值,这里创建5个元素的数组,初始值设置为1。
vector<string> a(5, "阿狸");   //使用string对象作为数组元素。
vector<int> a = {1,2,3,4,5};  //定义动态数组时赋值,数组元素个数由赋值数据个数确定。
#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> a = {1,2,3,4,5};/* 末尾添加一个元素 */a.push_back(6);printf("末尾元素:%d\n", a.back());/* 末尾删除一个元素 */a.pop_back();printf("末尾元素:%d\n", a.back());int b[5] = {6,7,8,9,10};/* 添加一组元素,参数1指定插入位置指针,参数2指定提供元素的数组起始地址,参数3指定提供元素的数组末尾地址 */a.insert(a.end(), &b[0], &b[5]);for(int i = 0; i < a.size(); i++){printf("%d\n", a[i]);}return 0;
}

字符串

string类实现字符串功能,其按照单字节的方式管理字符,双字节编码的字符可以使用wstring类。

创建string对象并赋值的常用方式:

string a = "阿狸";   //使用=符号赋值。
string a("阿狸");    //使用构造函数赋值,参数为字符串指针。
string a(b);        //使用构造函数赋值,参数为另一个string对象。

更多重载构造函数使用方式参考C++标准库手册。

常用成员函数如下:

at,使用下标调用数组元素,同时进行越界访问检查。
swap,与另一个string对象进行元素交换。
empty,判断字符串是否为空,返回布尔值,为空返回true,否则返回false。
size,返回字符串有效字符的个数,不包含末尾空字符。
length,同size。
data,返回第一个元素的指针。
c_str,同data。
front,返回第一个有效字符。
back,返回最后一个有效字符。
insert,在指定位置插入一组元素。
clear,清空字符串。

string还定义了很多成员符号函数,可以使用这些符号函数进行与符号含义相同的操作,比如使用 =、+、== 对string对象进行修改、连接、比较。

#include <iostream>
#include <string>
using namespace std;
int main()
{string ali = "阿狸";string xyy = "喜羊羊";string zoo = ali + xyy;printf("%s\n", zoo.data());if(zoo == "阿狸喜羊羊"){printf("相同\n");}else{printf("不同\n");}return 0;
}

转换 string 对象的全局函数

stoi,将表示数学数据的字符串转换为对应的 int 类型数据,并返回。
stol,同上,转换为 long 类型。
stoul,同上,转换为 unsigned long 类型。
stoll,同上,转换为 long long 类型。
stoull,同上,转换为 unsigned long long 类型。
stof,同上,转换为 float 类型。
stod,同上,转换为 double 类型。

to_string,将一个数学数据转换为 string 对象表示形式。
to_wstring,同上,转换为 wstring 对象。

#include <iostream>
#include <string>
using namespace std;
int main()
{string s1 = "10";int i1 = stoi(s1);int i2 = 100;string s2 = to_string(i2);return 0;
}

stack类以栈的方式管理数据,栈的特点是最后存储的数据最先使用,类似弹夹,常用函数如下:

push,入栈一个数据。
top,返回栈顶数据,数据不从栈内删除。
pop,删除栈顶数据,数据不返回。
size,返回栈内数据个数。
empty,判断栈是否为空,返回布尔值,为空返回true,否则返回false。
swap,与另一个栈整体交换数据。

#include <iostream>
#include <stack>
using namespace std;
int main()
{stack <int> a;for(int i = 0; i < 10; i++){a.push(i);}while(!a.empty()){printf("%d\n",a.top());a.pop();}return 0;
}

队列

queue类提供队列方式管理数据的功能,队列管理方式是首先存储的数据首先使用,就像排队一样,数据的使用遵守先来后到的原则,数据不支持使用下标的方式调用,常用函数如下:

push,入队一个数据。
front,返回队列第一个数据,数据不删除。
pop,删除队列第一个数据,数据不返回。
back,返回队列最后一个数据,数据不删除。
size,返回队列内数据个数。
empty,判断队列是否为空,返回布尔值,为空返回true,否则返回false。
swap,与另一个队列进行数据交换。

#include <iostream>
#include <queue>
using namespace std;
int main()
{queue <int> a;for(int i = 0; i < 10; i++){a.push(i);}while(!a.empty()){printf("%d\n",a.front());a.pop();}return 0;
}

双端队列

双端队列可以在头部、尾部新增数据,使用数据时也可以在头部、尾部使用,还可以在中间添加、删除数据,数据的管理方式更灵活。

deque类实现双端队列功能,常用函数如下:

push_front,在头部添加一个数据。
push_back,在末尾添加一个数据。
front,返回头部第一个数据,数据不删除。
back,返回末尾第一个数据,数据不删除。
pop_front,删除头部第一个数据,数据不返回。
pop_back,删除末尾第一个数据,数据不返回。
at,使用下标访问队列元素,并进行越界访问检查。
size,返回队列内数据个数。
empty,判断队列是否为空,返回布尔值,为空返回true,否则返回false。
begin,返回头部第一个数据的指针。
end,返回末尾第一个数据的指针+1,可以与insert函数配合使用。
insert,在指定位置插入数据(位置使用指针指定),此位置及之后的数据向后移动,此函数为重载函数。
erase,删除指定位置的数据(位置使用指针指定),此位置之后的数据向前移动,此函数为重载函数。
swap,与另一个双端队列进行数据交换。
clear,清空队列。

#include <iostream>
#include <deque>
using namespace std;
int main()
{deque <int> a;for(int i = 0; i < 10; i++){a.push_back(i);}a.insert(a.begin()+3, 99);    //在下标3处添加数据a.erase(a.end()-2);           //在末尾下标-1处删除数据while(!a.empty()){printf("%d\n",a.front());a.pop_front();}return 0;
}


列表

列表类似双端队列,但是实现原理不同,在列表中间插入数据时无需移动其它数据,缺点是不能随机访问列表中间的数据,只能在头部或尾部访问数据。

list类实现列表功能,常用成员函数如下:

push_front,在头部添加一个数据。
push_back,在末尾添加一个数据。
front,返回头部第一个数据,数据不删除。
back,返回末尾第一个数据,数据不删除。
pop_front,删除头部第一个数据,数据不返回。
pop_back,删除末尾第一个数据,数据不返回。
size,返回列表的长度。
empty,判断列表是否为空,返回布尔值,为空返回true,否则返回false。
begin,返回头部第一个数据的指针,返回值为指针常量,不能修改。
end,返回末尾第一个数据的指针+1,返回值为指针常量,不能修改。
insert,在指定位置插入数据。
erase,在指定位置删除数据。
splice,将另一个列表插入到此列表的指定位置。
remove,删除指定值的所有数据。
swap,与另一个列表进行数据交换。
clear,清空列表。

#include <iostream>
#include <list>
using namespace std;
int main()
{list <int> a = {1,2,3};list <int> b = {11,12,13};a.splice(a.end(), b);while(!a.empty()){printf("%d\n",a.front());a.pop_front();}return 0;
}


【智能指针】

C++使用new、delete简化了申请和释放内存的步骤,但是在代码非常复杂时依然很容易忘记释放内存,或者程序代码编写有问题导致不会执行delete,在大量申请内存时,内存使用完后不释放是很危险的,为了避免内存不被释放,C++增加了智能指针对象,智能指针可以自动管理申请的内存,其构造函数接收管理的内存地址,析构函数释放管理的内存。

智能指针定义在 memory 文件中,有如下三种:
unique_ptr,独占申请内存的管理权,不能与其它智能指针共同管理同一块内存,否则会出错。
shared_ptr,可以与其它shared_ptr对象共同管理同一块内存,内存不再使用后只被其中一个shared_ptr对象释放。
weak_ptr,在某些特殊情况下配合shared_ptr使用。

unique_ptr

常用成员函数如下:

get,返回管理的内存地址。
reset,修改管理的内存地址,原有内存会被释放,若修改的内存地址与原有内存地址相同,则内存也会被释放,reset不会判断修改值与原值是否相同。此函数为重载函数,若参数为空则用于释放内存。
release,退出此内存的管理权,并返回管理的内存地址,此函数不释放内存。

#include <iostream>
#include <string>
#include <memory>
using namespace std;
int main()
{unique_ptr <string> p1(new string);    //<>符号内设置指针类型,p1为智能指针对象名称unique_ptr <string> p2;*p1 = "阿狸";p2 = move(p1);    //转移内存管理权,注意不能使用=符号直接在两个unique_ptr对象之间赋值printf("%s\n", p2->data());return 0;
}

shared_ptr

多个shared_ptr对象可以同时管理同一块申请的内存,多个shared_ptr对象会使用一个数据记录共同管理内存的智能指针数量,在只剩下一个shared_ptr对象后才会去释放内存,从而防止内存被提前释放或多次释放。

注意,多个shared_ptr对象共同管理内存时并非直接将此内存的地址交由shared_ptr对象,而是在创建第一个shared_ptr对象时接收管理内存,新的shared_ptr对象引用其它已赋值shared_ptr对象进行赋值。

常用成员函数如下:

get,返回管理的内存地址。
reset,修改管理的内存地址,使用方式同unique_ptr。
use_count,查询并返回管理内存的shared_ptr对象数量,查询结果包含本对象。
unique,判断本对象是否为内存的唯一管理者,返回布尔值,是的话返回true,否则返回false。

shared_ptr对象设置管理内存的方式如下:
1.使用构造函数直接设置管理内存。
2.使用构造函数调用另一个shared_ptr对象设置管理内存。
3.创建空对象后使用reset成员函数设置管理内存。

#include <iostream>
#include <string>
#include <memory>
using namespace std;
int main()
{shared_ptr<string> p1(new string);*p1 = "阿狸";printf("%s\n""内存管理者数量:%d\n",p1->data(), p1.use_count());shared_ptr<string> p2(p1);    //引用p1创建p2printf("%s\n""内存管理者数量:%d\n",p2->data(), p1.use_count());return 0;
}

weak_ptr

weak_ptr用于配合shared_ptr使用,有如下两种作用:
1.创建一个只使用内存(有限使用)、不释放内存的智能指针,释放内存的工作交给其它shared_ptr对象执行。
2.解决shared_ptr对象的死锁问题,当两个对象都使用new申请内存存储、并分别使用一个shared_ptr成员管理对方时会形成死锁,导致两者都无法释放内存,此时可以将一个类中的shared_ptr成员改为weak_ptr。

使用注意事项:
1.weak_ptr对象不能操作内存中的数据,weak_ptr对内存的使用只限于查询此内存的相关属性,若需要使用此内存中的数据可以创建一个shared_ptr对象并使用其构造函数接收weak_ptr对象。
2.weak_ptr对象赋值使用的内存地址时只能调用其它shared_ptr对象赋值,不能直接接收一个内存地址,可以使用构造函数赋值、或者使用=符号函数赋值。

常用成员函数如下:

use_count,查询并返回管理此内存的shared_ptr对象数量,其中不包含本对象,weak_ptr对象没有内存管理权。
expired,查询并返回此内存是否有管理者,有则返回true,没有返回false。
reset,放弃内存的使用权,不再使用此内存。


死锁是一个计算机术语,表示两个行为互相制约,彼此都不能继续执行,好比你出门忘记带钥匙,需要开锁公司进行开锁,开锁公司开锁的前提是你提供房屋所有权证明,而你的证明文件锁在屋内,只有开锁之后才能出示所有权证明,此时开锁与出示所有权证明两个行为互相制约,在计算机中常见的情况是两个程序都需要独占两个文件使用权,程序1打开了文件1,程序2打开了文件2,此时两个程序都在等待对方关闭文件,从而形成死锁,使用shared_ptr对象管理内存时也会有死锁现象发生,比如下面的代码。

#include <iostream>
#include <memory>
using namespace std;class sheep;class fox
{
public:fox(){printf("fox构造函数\n");}~fox(){printf("fox析构函数\n");}shared_ptr<sheep> p1;//......
};class sheep
{
public:sheep(){printf("sheep构造函数\n");}~sheep(){printf("sheep析构函数\n");}shared_ptr<fox> p1;//......
};int main()
{shared_ptr<fox> ali(new fox);shared_ptr<sheep> xyy(new sheep);ali->p1 = xyy;xyy->p1 = ali;    //两个对象互相使用shared_ptr指向对方return 0;
}

两个对象内部的shared_ptr智能指针成员互相指向对方,彼此都在等待对方被释放,从而造成两者都不会释放,两个对象的构造函数都不会执行,将fox或sheep的shared_ptr对象成员改为weak_ptr即可解决问题,但是此时其中一方对内存的使用会受到限制。

智能指针的作用是方便我们编写代码,而不是为了增加程序代码的复杂度,在混乱的智能指针关系中可以选择使用普通指针,内存管理完全由自己掌控,注重执行效率的程序也应该使用普通指针自行管理内存。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/526508.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AcWing】蓝桥杯集训每日一题Day1|二分|差分|503.借教室(C++)

503. 借教室 503. 借教室 - AcWing题库难度&#xff1a;简单时/空限制&#xff1a;1s / 128MB总通过数&#xff1a;8052总尝试数&#xff1a;26311来源&#xff1a;NOIP2012提高组算法标签二分差分 题目内容 在大学期间&#xff0c;经常需要租借教室。 大到院系举办活动&…

【吊打面试官系列】Java虚拟机JVM篇 - 关于内存溢出

大家好&#xff0c;我是锋哥。今天分享关于内存溢出的JVM面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是内存溢出&#xff1f; 内存溢出&#xff08;OOM&#xff09;是指可用内存不足。程序运行需要使用的内存超出最大可用值&#xff0c;如果不进行处理就会影响到其他…

Django添加app

Django添加App python manage.py startapp [app_name]快速上手 注册app&#xff0c;setting.py 编写url和视图的对应关系 添加视图函数 命令行启动 python manage.py runserver页面模板

七、软考-系统架构设计师笔记-数据库设计基础知识

1、数据库基础概念 数据库基本概念 数据(Data)数据库(Database)数据库管理系统(DBMS)数据库系统(DBS) 1.数据(Data) 是数据库中存储的基本对象&#xff0c;是描述事物的符号记录。 数据的种类&#xff1a; 文本、图形、图像、音频、视频等。 2.数据库(Database, DB) 数据库…

LeetCode Python - 44.通配符匹配

目录 题目答案运行结果 题目 给你一个输入字符串 (s) 和一个字符模式 &#xff0c;请你实现一个支持 ‘?’ 和 ‘’ 匹配规则的通配符匹配&#xff1a; ‘?’ 可以匹配任何单个字符。 ’ 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09;。 判定匹配成功的充要…

数字化审计智慧

简析内部审计数字化转型的方法和路径 内部审计是一种独立的、客观的确认和咨询活动&#xff0c;包括鉴证、识别和分析问题以及提供管理建议和解决方案。狭义的数字化转型是指将企业经营管理和业务操作的各种行为、状态和结果用数字的形式来记录和存储&#xff0c;据此再对数据进…

Excel 一列数据转换为多行数据

目录 一. 需求二. 填充 替换 进行转换 一. 需求 ⏹有如下图所示的数据&#xff0c;现在要求按每5行为一组&#xff0c;转换为一行 二. 填充 替换 进行转换 先手动将一列中的1到5行的单元格号转换为一行再手动将一列中的6到10行的单元格号转换为一行然后填充最后使用替换功能…

为什么选择 Flink 做实时处理

优质博文&#xff1a;IT-BLOG-CN 为什么选择 Flink 【1】流数据更真实地反映了我们的生活方式&#xff08;实时聊天&#xff09;&#xff1b; 【2】传统的数据架构是基于有限数据集的&#xff08;Spark 是基于微批次数据处理&#xff09;&#xff1b; 【3】我们的目标&#xf…

算法(递归)黑盒思想

递归vs搜索vs回溯 递归的时候其实就是在搜索&#xff0c;递归返回的时候其实就是在回溯 常见的二叉树的题目基本都用到了递归&#xff1a; 求二叉树节点个数&#xff08;后序遍历&#xff09; int BinaryTreeSize(BTNode* root) {return root NULL ? 0 : BinaryTreeSize(roo…

爬虫(五)

1. 前端JS相关 三元运算 v1 条件 ? 值A : 值B; # 如果条件成立v1值A&#xff0c;不成立v1等于值Bres 1 1 ? 99 : 88 # res99特殊的逻辑运算 v1 11 || 22 # Ture v2 9 || 14 # 9 v3 0 || 15 # 15 v3 0 || 15 || "zhangfei" # 15赋值和…

使用Migration升级数据库

使用Migration升级数据库 package com.tiger.room2;import android.content.Context;import androidx.annotation.NonNull; import androidx.room.Database; import androidx.room.Room; import androidx.room.RoomDatabase; import androidx.room.migration.Migration; impo…

嵌入式 Linux 学习

在学习嵌入式 Linux 之前&#xff0c;我们先来了解一下嵌入式 Linux 有哪些东西。 1. 嵌入式 Linux 的组成 嵌入式 Linux 系统&#xff0c;就相当于一套完整的 PC 软件系统。 无论你是 Linux 电脑还是 windows 电脑&#xff0c;它们在软件方面的组成都是类似的。 我们一开电…