C++ 快速入门
先有C语言基础,注意vc6要用管理员身份运行。
typora需要在偏好设置里面开markdown 内联公式。
建议跳着看
1. 基础
-
库将不同用途工具放在不同的箱子里,实现解耦。
-
不同的库可能有同名的函数,所以要用名字空间区分。
-
C++ 兼容C语言。
-
C++中的标准输入输出是通过输入输出库中的输入输出流对象实现的。
类、函数和变量便是c++编译器的标准组件,它们现在都放置在名字空间std中。这意味着cout输出函数实际上是std::cout,因此可以直接用std::cout<<而不使用using namespace std;
声明自己需要用到的函数,比如:using std::cout
1. 输入输出基本方法
#include <iostream> //包含库文件
using namespace std; //使用名字空间(命名空间)
int main(){
int a;
cout << "你多少岁?" << endl ; //endl是换行+清空缓冲区
// << 流插入运算符
cin >> a;
//>> 流提取运算符
cout << a << endl;
return 0;
}
2. 常见问题
-
c++20中auto可定义迭代器变量,自动推导变量类型。
-
类名与变量名重合。
int A=5;//改变量名就行
A b;
- 函数命名尽量不要跟标准函数名相同。
2. 数据类型
bool 布尔类型
bool的取值只能为true(非0)或false(0)
#include <iostream>
using namespace std;
int main(){
bool a=2;//等价于bool a=true;或bool a=1;
cout << a;//输出1
return 0;
}
string 类
容器那章有一般通用函数的说明。
#include <string>
#include<iostream>
using namespace std;
int main(){
string a="ABCD";
cout << a.length() << endl;//返回字符串长度,4(不计\0)
a[2]='c';//ABcD,修改字符串
cout<<a.append(" EFG")<<endl;//添加至末尾
cout<<a.find("EFG")<<endl;//查找字符串位置(E在5号位)
string sub=a.substr(2,4);//截取一个子字符串
cout<<sub<<endl;//cD E
a.erase(1,3);//删除一个子字符串
cout<<a<<endl;//A EFG
a.replace(0,4,"Hi");//替换一个字符串
cout<<a<<endl;//HiG
return 0;
}
宽字符类型
需包含头文件#include <cwchar>
和#include<locale>
用于存储unicode字符编码值,一般2或4字节
相关的一些输入输出流:wcin,wcerr,wofstream,wifstream
#include <cwchar>
#include <iostream>
#include <locale>//软件运行时的语言环境
using namespace std;
locale loc("chs");
int main(){
wcin.imbue(locale("",LC_CTYPE));//将wcin的locale为本地语言
wcout.imbue(loc);//设置wcout的locale
wchar_t c[]=L",启动!";
wchar_t b[1000];
//wcin>>b;这个一般只能英文字符
wcin.getline(b,4);//可以读取两个中文字符,比如原神
wcout<<b<<c<<L"\n";
return 0;
}
3. 条件循环语句
略。
4. 函数
1. 默认参数
设置默认参数时,应当从右向左设置。
int power(int n,int b=6);//默认参数要放在最后
2. 引用传参
特指左值引用。(给变量起别名)
int & 整型引用
int& b=a等价于int* const b=&a,指针常量的指针指向不变。
int a=10;
int &b=a,&c=b;//c是a的一个引用(别名)
cout << b << endl;//输出10
c++;
cout <<a;//输出11
void swap(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
void swapp(int *a,int *b){
int temp=*a;
*a=*b;
*b=temp;
}
int main(){
int m=1,n=2;
swap(m,n);
cout<<m<<" " <<n<< endl;
swapp(&m,&n);
cout<<m<<" " <<n<< endl;
return 0;
}
2 1
1 2
3. 函数重载overload
方便调用方编写代码,提高可读性。
特征:
- 函数名相同,参数列表不同。
- 不产生匹配歧义。
int add(int a);
int add(int a,int b);
int add(char a,int b);
int add(int a,int b,int c);
overload歧义
解决:不允许仅有返回值不同的函数重载。
int fc(int a,int b);
int fc(int &a,int &b);
int fc(int a,int b,int c=3);
string fc(int a,int b);
//以上四种种写法之间会产生歧义
4. 内联函数 inline
当一个函数语句少但是用的很频繁就要用内联函数。
内联指建议编译器编译时将某个函数在调用处直接展开,避免运行时调用开销。(编译器不一定会采纳)。
inline int getmax(int a,int b,int c){
return a>b?(a>c?a:c):(b>c?b:c);
}
int main(){
cout << getmax(1,2,3)<<endl;
return 0;
}
5. 匿名函数
格式:
[捕获列表](参数列表)->返回类型{return ...;};
或者
[捕获列表](参数列表){return ...;};
[&] //所有都引用捕获
[=] //所有都按值捕获
[&,=N] //单独指定按值捕获,其它都引用捕获
[&N,M] [M,&N] [&,=M] [=,&N]
//引用捕获N,按值捕获M
//在class中使用匿名函数
[this]//捕获当前实例的指针
[*this]//按值捕获当前实例
[K=5]//定义新变量并初始化
int a=10,b=20;
auto g=[a,&b](int m,int n){
//a是按值捕获,不能修改外围变量的值
//b是按引用捕获,可以修改外围变量的值
b=50;
return a*m+n*b;
};
cout<<g(10,2)<<endl;//200
cout<<b<<endl;//50
5. 面向对象
面向对象程序设计有4个主要特点:抽象、封装、继承和多态性
1. UML类图
顺序:类名,属性(成员变量),行为(成员方法)。
2. 类与对象
#include <string>
#include<iostream>
using namespace std;
class Circle{
public:
float radius;
Circle(float radius){
this->radius=radius;
}//构造函数
float getS(){
return 3.14*radius*radius;
}
float getC(){
return 2*3.14*radius;
}
};//注意分号
int main(){
Circle c1(1.f),c2(2.5f);//实例化对象
cout <<c1.getS()<<endl<<c1.getC()<<endl;
}
构造函数与析构函数
构造函数创建对象时自动调用,可以有多个重载,不能有返回值。
class A{
public:
int n;
char * data=nullptr;
A(int n){//构造函数必须与类名相同
this->n=n;//this指当前对象(类)的n,而不是A函数的形参。
data=(char*)malloc(100);
}
~A(){
free(data);//析构函数
}
};
3. 封装
#include <string>
#include<iostream>
using namespace std;
class Book{
private://private:可以省略
string name;
int count;
public:
Book(string name){
this->count=0;
this->name=name;
}
void set_name(string name){
this->name=name;
}//setter方法
void set_count(int count){
this->count=count;
}
string get_name()const{//常成员函数,不能修改类成员变量
return name;
}
int get_count()const{//常成员函数,不能修改类成员变量
return count;
}//getter方法
};
int main(){
Book b1("海底两万里");
b1.set_count(30);
cout<<b1.get_name()<<endl;
cout<<b1.get_count()<<endl;
return 0;
}
4. 类的派生与继承
目的:代码复用
派生:不断补充
基类:父类
#include <string>
#include<iostream>
using namespace std;
class A{
public:
int nA;
A(){
nA=1;
}
void fa(){
cout<<"fa\n";
}
};
class B:public A{//B继承A
public:
int nB;
B(){
nB=1;
}
void fb(){
cout<<"fb\n";
}
void print(){
cout<<"打印B";
}
};
class C:public B{//C继承B,公有继承
public:
int nC;
C(){
nC=1;
}
void fc(){
cout<<"fc\n";
}
void print(){
cout<<"打印C";
}
};
int main(){
C c;
c.fa();
c.fb();
c.fc();
//如果出现同名方法或成员属性
c.print();//直接使用默认子类成员
c.B::print();//使用父类名字空间指明
return 0;
}
继承方式
公有继承public,保护继承protected,私有继承private
父类的private成员不会被子类继承。
父类的public成员在保护继承时会被降级成protected。
class A{
private:
int k;
public:
void set(int n){k=n;}
int get()const{return k;}
};
class B:protected A{//保护继承
protected:
int j;
public:
void set(int m,int n){A::set(m);j=n;}
int get()const{return A::get()+j;}
};
//B类中保护成员数量为3(继承自A的set,get和本身的j)
总结:1. 父类private成员不被继承,其他成员按继承方式不同降级或不变。
-
通过派生类对象访问的(外部以a.xxx方式访问):公有继承的基类公有成员。
-
通过派生类能访问的(类定义内部访问):除了基类私有成员以外的都可以访问。
5. 虚函数 virtual
父类虚函数可以在子类中被重写(override),但参数和返回值必须保持一致。
纯虚函数:不实现,仅声明为纯虚函数,留给子类重写
含有纯虚函数的类叫抽象类,仅有纯虚函数的类叫接口。
抽象类不能被实例化。
子类重写基类虚函数时,可以继续加virtual关键字声明,表示允许子类继续重写。
class person{
public:
virtual void say(){//虚函数
cout<<"person";
}
virtual float getS()=0;//纯虚函数
};
class stu:public person{
public:
float radius;
void say(){
cout<<"stu";
}
float getS(){
return 3.14*radius*radius;
}
};
int main(){
stu s;
s.say();
return 0;
}
6. 多态
联编:确定同名函数用哪个
目的:实现代码复用
- 运行时多态(动态联编):虚函数重写+指向子类对象的父类指针
- 编译时多态(静态联编):函数重载(overload)
通过指针调用的是对象本质子类的方法。
#include <string>
#include<iostream>
using namespace std;
class shape{
public:
virtual float getS()=0;
float r;//这里不能初始化
};
class rectangle:public shape{
public:
float a,b;
rectangle(float a,float b){
this->a=a;
this->b=b;
}
float getS(){
return a*b;
}
};
class circle:public shape{
public:
float r;
circle(float r){
this->r=r;
}
float getS(){
return 3.14*r*r;
}
};
void display(shape *p){//实现多态,父类指针调用子类重写的虚函数
cout << p->getS()<<endl;
}
int main(){
circle c(10.0);
rectangle r(5.5,6.0);
display(&c);
display(&r);
shape *p=(shape *)&c;
cout<< p->getS();//调用本质circle的getS()方法,r是circle中的r
return 0;
}
7. 友元
友元使非成员函数或非本类成员函数可以访问本类的私有成员。
-
友元关系不能被继承。
-
友元关系是单向的。
-
友元关系不具有传递性。
友元函数
#include <string>
#include<iostream>
using namespace std;
class A{
double a;//这个a是私有成员
public:
friend void print(A m);//声明友元函数
void seta(double a){
this->a=a;
}
};//注意类定义的分号
//定义友元函数
void print(A m){
cout<<m.a<<endl;//访问A的私有属性
}
int main(){
A k;
k.seta(9.5);
print(k);
return 0;
}
友元类
#include <string>
#include<iostream>
using namespace std;
class A{
double get(double a){
return a;
}
public:
friend class B;//类B能使用A的私有成员
};//注意类定义的分号
//定义友元类
class B{
double b;
public:
void set(double b){
this->b=b;
}
void print(){
cout<<A().get(b);//创建一个匿名对象并访问A的私有方法
}
};
int main(){
B k;
k.set(5.0);
k.print();
return 0;
}
成员函数做友元
friend void A::show(B&);//将A类的show()声明为B类的友元函数
8. 模板函数与模板类
模板函数和模板类可以接受不同的参数类型
使用时需指定参数类型
#include <string>
#include<iostream>
#include<vector>
using namespace std;
template<typename T>//声明模板函数,T为模板参数
T mymax(T a, T b) {//返回类型不一定要T
return a > b ? a : b;
}
template <typename T,typename T1>
class A {
public:
void pt(T v1,T1 v2) {
cout << v1<< "\t" << v2 << endl;
}
T rt(T value) {
return value;
}
};
int main() {
int a = 10, b = 20;
cout << mymax<int>(a, b) << endl; // 输出 20
//使用模板函数时,需要在函数名后面加上尖括号 <T> 来指定模板参数的类型
double c = 3.14, d = 2.718;
cout << mymax<double>(c, d) << endl; // 输出 3.14
A<string,int> dt;
cout << dt.rt("hello") << endl;
dt.pt("vivo",50);
return 0;
}
9. operator与函数对象
C++11以上
1. 函数对象(仿函数)
概念:用类创建出来的一个对象,调用起来语法结构像调用函数,我们把这种对象叫做函数对象。它通过重载函数调用操作符(operator())来实现可调用操作。
本质是一个对象。
特点:
- 函数对象在使用的时候,可以像普通的函数那样调用,可以有参数,可以有返回值
- 函数对象超出普通函数的概念,可以有自己的状态
- 函数对象可以作为参数传递
2. operator做类的转换函数
operator 类型(){}
将类的对象转化为其他的类型。
要求:
- 转换函数必须是类方法
- 转换函数不能指定返回类型
- 转换函数不能有参数列表
#include<iostream>
#include<string>
using namespace std;
class A {
public:
int count;
A() :count(0) {}//:count(0)为初始化列表部分
operator int() {//定义了一个将类转化为int的转换函数
cout << "to_int" << endl;
return 1;
}
explicit operator double() {
cout << "explicit 防止隐式转换出现二义性" << endl;
return 1.5;
}
string operator()(string a, string b) {//这个写法是合法的
return a.append(b);
}
};
int main() {
A a;//函数对象(仿函数)
int b = int(a);//显式转换
cout << b << endl;
cout << a << endl;//隐式转换
cout << (double)a << endl;//显式转换
cout << a("vivo ", "50!") << endl;//vivo 50!
return 0;
}
3. operator在类中重载运算符
类型 operator 运算符(参数)
重载主要有两种形式,成员函数形式与友元函数形式。
#include <iostream>
using namespace std;
class MyClass
{
public:
int a;
double b;
MyClass() {}//无参构造
MyClass(int a, double b) :a(a), b(b) {}//a(a),b(b)是初始化列表,后面{}里面可实现功能
~MyClass() {}
MyClass operator+(const MyClass& adder) const//以成员函数方式重载+
{
MyClass sum;
sum.a = a + adder.a;
sum.b = b + adder.b;
return sum;
}
friend MyClass operator-(const MyClass& A, const MyClass& B)//以友元方式重载-
{
MyClass diff;
diff.a = A.a - B.a;
diff.b = A.b - B.b;
return diff;
}
};
int main()
{
MyClass A(1, 1.1);
MyClass B(2, 2.2);
MyClass sum = A + B;
MyClass diff = A - B;
cout << sum.a << "\t" << sum.b << endl;//3 3.3
cout << diff.a << "\t" << diff.b << endl;//-1 -1.1
return 0;
}
6. 文件读写
windows \,Linux /
ifstream
读文件
ofstream
写文件
fstream
读写文件
一些函数
seekg(5L,ios::beg);//(编号从0开始)从文件头开始偏移5(实际第6字节)
seekg() | 读的时候将文件指针移动到指定位置。 |
---|---|
seekp() | 写的时候将文件指针移动到指定位置。 |
tellg() | :获取当前文件指针的位置。 |
eof() | :判断是否已经达到文件末尾。 |
good() | :判断是否操作成功。 |
模式
ofstream fp(“example.txt”,ios::in|ios::binary);
std::ios::in | 读取模式 |
---|---|
std::ios::out | 写入模式。文件不存在,新建文件。存在,清空文件内容 |
std::ios::app | 追加模式打开。文件不存在,新建文件。存在,在末尾追加 |
std::ios::binary | 二进制模式打开。 |
std::ios::ate | 打开文件后,文件指针置于末尾 |
std::ios::trunc | 写入模式。文件不存在,新建文件。存在,清空文件内容 |
std::ios::beg | 从文件头开始计算偏移量 |
std::ios::end | 从文件末尾开始计算偏移量 |
std::ios::cur | 从当前位置开始计算偏移量 |
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
// 写入文件
ofstream fp("./1.txt");
if (fp.is_open()) {
fp << "Hello, World!" << endl;
fp.close();
} else {
cout << "打不开" << endl;
}
// 读取文件
string line;
ifstream p("./1.txt");
if (p.is_open()) {
while (getline(p, line)) {
cout << line << endl;
}
p.close();
} else {
cout << "打不开" << endl;
}
return 0;
}
7. C++内存管理
C/C++内存被分为6个区域:
(1)内核空间存放内核代码和环境变量
(2) 栈(也叫堆栈)存放非静态局部变量/函数参数/返回值等等,栈是向下增长的。
(3)内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
(4)堆用于程序运行时动态内存分配,堆是向上增长的。
(5)数据段存储全局数据和静态数据。
(6)代码段存放可执行的代码/只读常量。
new与delete
对内置类型的操作,new/delete和malloc/free没有区别
如果为自定义类型申请空间,区别很大:
①new和是开空间+初始化,delete是析构清理+释放空间
②malloc仅仅是开空间,free仅仅是释放空间
#include<iostream>
using namespace std;
int main()
{
//操作内置类型
//1.申请单个int对象
int* p1 = (int*)malloc(sizeof(int));
free(p1);
int* p2 = new int;//int* p2 = new int(10); 申请单个int对象并将其初始化为10
delete p2;
//2.申请10个元素的int数组
int* p3 = (int*)malloc(sizeof(int) * 10);
free(p3);
int* p4 = new int[5];//如果new申请失败直接抛异常
//C++11支持用{}对数组初始化即int *p4=new int[5]{1,2,3,4};
delete[] p4;//自定义类型数组delete要带上[],否则不匹配,程序会意外终止
return 0;
}
内存泄漏
简单理解就是指针没了,指针指向的内存还在(没有free掉那段内存)。
危害:造成内存浪费,响应卡顿。
8. STL-常用容器
这章用visual studio 2022
容器一般通用函数
比如:v.push_back(6);
push_back(5) | 末尾添加一个元素5 |
---|---|
pop_back() | 末尾删除一个元素 |
dq.push_front(-1); | 在队头添加元素-1 |
dq.pop_front(); | 删去队头元素 |
size() | 返回元素个数 |
b.resize(5); | 更改b的大小为5,若小于原大小则删除多余元素,array不能用 |
b.resize(5,0); | 更改b的大小为5,若大于原大小则新元素初始化为0,array不能用 |
empty() | 判断vector是否为空 |
clear() | 清空vector所有元素 |
front() | 返回第一个元素 |
back() | 返回最后一个元素 |
at(2) | 返回位置2(从0计)的元素,越界则抛异常 |
v.erase(v.begin()+1); | 删除指定位置的元素 |
l.remove(8) | 删除所有元素8 |
v.insert(v.begin()+1,5); | 在位置1前面添加元素5 |
fill_n(v.begin(),5,4); | 填充5个4 |
a.swap(b); | 交换容器a,b内容 |
auto p=v.begin(); | 返回一个指向容器第一个元素的迭代器,可以用解引用操作符*获取值(cout<<*p; ) |
int* p=v.end(); | 返回一个容器末尾的迭代器,表示元素最后一个元素的下一个位置 |
rbegin() | 返回指向容器逆序起始位置的逆序迭代器 |
rend() | 返回指向容器逆序末尾位置的逆序迭代器 |
容器迭代器分类
- 单向(Unidirectional Iterator):单链表 forward_list、哈希表 unordered_set/map 迭代器,可以 + +
- 双向(Bidirectional Iterator):双向链表 list、红黑树 set/map 迭代器,可以 + + / - -
- 随机(Random Access Iterator):string、vector、deque 迭代器,一般容器的底层都是连续的数组,可以 + + / - - / + / -
1. vector 向量
#include
vector<储存的类型> 容器名
初始化
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> v1;//可看成动态一维数组
vector<float> v2(3);//初始化一个有三个元素的vector
vector<char> v3(3,'a');//初始化一个有三个'a'的vector,与类区分
vector<char> v4(v3);//v3元素拷贝到v4
return 0;
}
示例
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b){
return a>b;//从大到小排序
}
void print(vector<int> v){
size_t i;
for(i=0;i<v.size();i++){
cout<<v[i]<<' ';
}
cout<<endl;
}
int main(){
vector<int> a;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(4);
a.push_back(5);
sort(a.begin(),a.end(),cmp);
print(a);//5 4 3 2 1
vector<int>::iterator b=a.begin();
cout<<*b<<endl;//解引用获取值5
return 0;
}
2. algorithm库
sort(首元素地址,尾元素地址的下一个地址,比较函数(非必填)) | 排序容器元素,默认从小到大排 |
---|---|
int* c=find(a.begin(),a.end(),1); | 查找6所在编号(编号从0开始计),返回值为迭代器类型,用指针指向它 |
int sum=accumulate(v.begin(),v.end(),4); | 累加求和并加上4 |
string a=accumulate(v.begin(),v.end(),string(" ")); | 从空字符串开始,把v里的每个元素连接成一个字符串 |
copy(v1.begin(),v1.end(),v2.begin()); | 把v1复制到v2 |
random_shuffle(v.begin(),v.end()); | 随机打乱,前面要加种子#include srand((unsigned int)time(NULL)); |
transform(a.begin(),a.end(),b.begin(),r); | 对v每个元素运用一个自定义函数r,结果存在v2 |
reverse(v.begin(),v.end()); | 反转元素 |
int* p=unique(v.begin(),v.end()); | 删除重复元素,返回指向最后一个不重复元素之后一个位置的迭代器 |
int n=count(v.begin(),v.end(),4); | 返回元素4的出现次数 |
replace(v.begin(),v.end(),3,7); | 把元素3替换成元素7 |
for_each(v.begin(),v.end(),func); | 对容器每个元素运用一个函数 |
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int r(const int& a){
return a*a;
}
void print(vector<char>& v){
size_t i;//无符号整数
for(i=0;i<v.size();i++){
cout<< v.at(i)<<' ';
}
cout<<endl;
}
int main(){
vector<int> a,b;
for(int i=1;i<6;i++){
a.push_back(i);
}
int* c=find(a.begin(),a.end(),1);//find返回值为迭代器类型,用指针指向它
//可以auto*
if(c!=a.end()){
cout<<"yes "<<c-a.begin()<<endl;//yes 0
}else{
cout<<"no "<<endl;
}
b.resize(a.size());//重新初始化a容器大小的b
transform(a.begin(),a.end(),b.begin(),r);
//print(b);
vector<char> d(5,'d');
print(d);
return 0;
}
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
void print(vector<int> b) {
for (auto a : b) {
cout << a<<" ";
}
cout << endl;
}
int main() {
vector<int> s1 = { 1,2,3,5,7,9 };
vector<int> s2 = { 2,4,6,8,10,1 };
sort(s1.begin(), s1.end());
sort(s2.begin(), s2.end());
vector<int> s3(min(s1.size(), s2.size()));
set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), s3.begin());
//有序序列取交集,返回一个最后一个元素迭代器
vector<int> s4(s1.size() + s2.size());
set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), s4.begin());
//有序序列取并集,返回一个最后一个元素迭代器
vector<int> s5(max(s1.size(), s2.size()));
set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), s5.begin());
//有序序列取差集,返回一个最后一个元素迭代器
print(s3);
print(s4);
print(s5);
return 0;
}
3. deque 双端队列
#include
创建方法
deque<int> dq;
deque<int> dq1={1,2,3};
deque<int> dq2(5);//容器大小为5
fill_n(dq2.begin(),5,10);//5个10
deque<int> dq3(10,6);//10个6
vector<int> v(5,3);
deque<int> dq4(v.begin(),v.end());
4. list 列表
#include
是线性的双向链表的数据结构。拥有链式结构的特征:支持元素的快速插入和删除,但是元素随机访问较慢(相较于vector容器),不提供[]运算符的重载。
创建列表
list<int> l;
list<int> l2(4,100);
list<int> l3(l2.begin(),l2.end());
list<int> l4(l3);
l1.merge(l2);//合并l2到l1。l1,l2必须提前排好序
5. array 数组
#include
大小固定,不能用resize()
创建方法
array<int,6> arr;
fill(arr.begin(),arr.end());
cout << *arr.data();//data()获取指向第一个元素的指针
array<int,4> arr2={1,2,3,4};
array<int,4> arr3=arr2;//两边array大小必须相等
array<int, 6>::iterator a = l.begin();
6. string 字符串
看前面string类。
7. set 集合
#include
- 集合不重复,按一定次序存储元素,不能直接设置大小。
- 插入时只需要插入value(底层存放实际是<value,value>形式键值对)。
- 元素默认按小于比较。
- 查找元素时间复杂度 O ( log 2 n ) O(\log_2 n) O(log2n)。
- 元素不可修改。
底层使用二叉搜索树(红黑树)。
STL-模板类pair
#include
成对的数据,利用队组返回两个数据。
#include <utility>
#include <iostream>
int main() {
std::pair<int, double> mypair(1, 3.14); // 创建 std::pair
std::pair<int,double> p=make_pair(9,5.5);
std::cout << "The first element is " << mypair.first << '\n';
// 输出第一个元素
std::cout << "The second element is " << mypair.second << '\n';
// 输出第二个元素
return 0;
}
set创建方法
#include <utility>
#include <iostream>
#include<set>
#include<functional>
using namespace std;
class great {
public:
bool operator()(int a, int b) const{
return a > b;
}
};
int main() {
set<int> s1;
pair<set<int>::iterator, bool>a = s1.insert(6);//插入元素6
//如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>
cout << *a.first << endl;//6
set<int> s2 = { 1,2,3,4,5,6 };//默认从小到大排序
s2.erase(3);//删除元素3
s2.erase(s2.begin(), s2.end());
//s2.end()指向末尾的下一个位置,删除[a,b)区间的元素
set<int> s3(s2.begin(), s2.end());
cout << s3.empty();//1
set<int, great> s4 = {1,2,3,4,5,6};//用仿函数由大到小排序
//set<int, greater<int>> s4 或者可以用算数仿函数
for (auto a : s4) {
cout << a;//输出654321
}
return 0;
}
8. map 映射
#include
- map中的的元素是键值对
- map中的key是唯一的,并且不能修改
- 默认按照小于的方式对key进行比较
- map中的元素如果用迭代器去遍历,可以得到一个有序的序列
- map的底层为平衡搜索树(红黑树),查找效率比较高 O ( log 2 N ) O(\log_{2} N) O(log2N)
- 支持[]操作符,operator[]中实际进行插入查找。
创建方法
map<string, int> mp1;
mp1.insert(pair<string, int>("apple", 3));
mp1["left"];//插入,value初始为0
mp1["right"] = 2;//插入+修改
int a = mp1["apple"];//3
cout << a;
map<string, int> mp2 = {{"apple",3},{"banana",2} };
map<string, int> mp3{{"apple",3}, { "banana",2 }};
int main() {
string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };
map<string, int> mp2;
for (string& e : arr) {//auto& e : arr
mp2[e]++;
}
for (pair<string,int> kv : mp2) {//auto& kv : mp2
cout << kv.first << ":" << kv.second << endl;//统计各种水果数量
}
return 0;
}
9. STL-仿函数
先理解operator与函数对象。
需要#include
-
返回bool类型的仿函数称为谓词。
-
如果operator()接受两个参数则称为二元谓词。
1. 算数仿函数
template T plus 加法仿函数
minus 减法
multiplies 乘法
divides 除法
modulus 取模
negate 取反
#include<iostream>
#include<functional>
using namespace std;
void t(int a,int b){
minus<int>n;//减法仿函数,对象名为n
cout<<"结果:"<<n(a,b)<<endl;
}
int main(){
t(50,40);//结果:10
return 0;
}
2. 关系仿函数
template bool equal_to 等于仿函数
not_equal_to 不等于
greater 大于
greater_equal 大于等于
less 小于
less_equal 等于
#include<iostream>
#include<functional>
#include<algorithm>
#include<vector>
using namespace std;
int main() {
vector<int> a;
for (int i = 0; i < 5; i++) {
a.push_back(i);
}
sort(a.begin(), a.end(), greater<int>());//greater<int>()匿名函数对象
for (vector<int>::iterator i = a.begin(); i < a.end(); i++) {
cout << *i << "\t";
}
cout << endl;
return 0;
}
3. 逻辑仿函数
template bool logical_and 逻辑与仿函数
logical_or 逻辑或
logical_not 逻辑非
#include<iostream>
#include<functional>
#include<algorithm>
#include<vector>
using namespace std;
int main() {
vector<bool> a, b;
for (int i = 0; i < 8; i++) {
a.push_back(bool(i % 2));
}
b.resize(a.size());//改b大小
transform(a.begin(), a.end(), b.begin(), logical_not<bool>());
for (vector<bool>::iterator i = b.begin(); i < b.end(); i++) {
cout << *i << "\t";
}
cout << endl;
return 0;
}