C++概述
C++介绍及环境搭建
C++发展历史
从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言,并且凭借这接近C语言的效率,在工业界使用的开发语言中占据了相当大的份额。
从1995年到2000年,这一阶段由于标准模板(STL)和后来的Boost等程序库的出现,泛型设计在C++中占据了越来越多的比重。
2000年至今,由于Loki、MPL等程序库为代表产生试编程和模板元编程的出现,C++出现了发展历史上又一个新的高峰。
当今主流程序设计语言中最为复杂的一员
C++环境搭建
在Ubuntu的终端下,输入命令
sudo apt-get install g++
即可安装g++编译器
输入命令
g++ -v
检测是否安装成功(成功则打印相关版本号)
g++的介绍:是一款与gcc类似的编译软件,用于编译C++的源代码,使用的方法与gcc类似。
命名空间
命名空间的概念
命名空间是用来防止大型项目中出现重名的函数、变量或类。
比如说,同一个项目的不同模块中出现同名函数或者全局变量,是不可避免发生的情况。而命名空间恰好能解决这个问题
命名空间中的声明
关键字namespace后指定空间名,大括号里进行各种声明。
namespace 空间名
{
//可以在此声明函数、变量、结构体
}
命名空间中函数的定义
在命名空间中声明的函数在定义时都必须在函数名前加上“空间名::”,以此来与全局或其他命名空间的函数进行区别。样式如下:
返回值类型 空间名::函数名(参数列表)
{
//函数体
}
命名空间的指定
可以使用作用域运算符“::”来指定命名空间
iotek::func();
可以使用using关键字指定命名空间的部分声明
using iotek::func;
func();//调用iotek命名空间下的func()
可以使用using关键字指定命名空间内的全部声明
using namespace iotek;
func();//调用iotek命名空间下的func()
C++输入输出
cin和cout的概念
由于C++兼容C语言,所以我们仍然可以使用scanf和printf进行输入输出。然而,C++本身也有对标准输入输出进行封装,他们就是cin和cout。
在C++中更推荐使用cin和cout来进行标准输入和输出
使用cin和cout的准备工作
#include <iostream>
using namespace std;
使用cout输出
cout可以用于多种数据类型的输出,包括所有的基本数据类型,字符数组以及string类型
输出单个对象的形式:
count << 要输出的对象;
输入多个对象凭借的形式:
cout << 要输出的对象 << 要输出的对象;
换行输出的形式:
cout << 要输出的对象 << endl;//此处endl表示换行
使用cin输入
cin可以用于多种数据类型的输入,包括所有的基本数据类型,字符数组以及string类型
输入单个基本数据类型:
cin >> 存放输入数据的对象;
输入单个字符串
cin >> 存放数据的对象;
注意:通过cin使用右移运算符“>>”进行输入时,默认“空白”为输入结束。“空白”指tab,空格和回车
面向对象编程基础
类和类的成员
类的概念
类是对数据的一种抽象,所谓抽象就是将一些事物中不需要的数据剔除,而留下程序需要的。
比如说,我们想写一个对学生成绩的管理系统,在整个系统中我们可能需要对多个学生的数据进行管理。然而我们只对学生的姓名和成绩感兴趣,而不需要性别和身高这些数据,所以说我们可以对学生的数据进行一个抽象,也就是说在程序中写一个类来描述学生。
类的声明
声明一个类的语法:
class 类名
{
private:
//私有的成员
public:
//公有的成员
};
类的成员
在类中可以声明任意类型的变量,即成员变量,表示这个类的属性。比如说学生可以有一个整形变量表示id,字符数组表示姓名,浮点型变量表示成绩。
在类中可以声明函数,即成员函数,表示这个类的行为。比如说学生可以有一个print函数打印自己的成绩。
类的定义
主要是对类中的成员函数进行定义
定义类成员函数的语法:
返回值类型 类名 ::函数名(参数列表)
{
//函数体
}
定义实例
void Student::print()//打印学生成绩
{
cout<<name<<","<<sore<<endl;
}
定义类成员函数的注意事项
当类成员函数定义在类的外部时,一定要在函数名前加上“类名::”,以此来表示该函数是类的成员函数。
在类的成员函数中可以自由的访问该类的其他成员属性和成员函数。
类成员函数的定义一般写在头文件对应的.cpp文件中。
对象和对象的使用(一)
对象的概念
从广义上讲,要在内存上有一段有意义的区域就称之为对象
在C++中,对象一般是指类在内存中装载的实例,具有相关的成员变量和成员函数。类是抽象的概念,而对象是通过类实现的具体实例。
比如说,学生是类,学生小明是对象。
构造函数和析构函数
由于对象一定会在内存中占用一段空间,所以一定会有其声明周期。也就是说对象一定有申请空间和释放空间的步骤
构造函数是当对象申请内存空间之后自动调用的函数。
析构函数是当对象的空间即将被销毁前自动调用的函数。
构造函数的声明
构造函数的声明需要在类中声明。
构造函数没有返回值。
构造函数的函数名必须和类名一致。
构造函数的定义
构造函数可以直接定义在类内部,也可以直接定义在类的外部。
定义在类内部的方式:
类名(参数)
{
//函数体
}
定义在类外部的方式:
类名::类名(参数列表)
{
//函数体
}
析构函数的声明
析构函数的函数以~+类名。
析构函数没有返回值。
析构函数没有参数列表。
析构函数的定义
析构函数既可以在类的内部定义也可以在类的外部定义。
类内部定义:
~类名(参数列表)
{
//函数体
}
类外部定义:
类名::~类名(参数列表)
{
//函数体
}
对象调用成员
可以通过运算符“.”或者“->”访问对象里的成员
对象和对象的使用(二)
构造函数特点
构造函数的函数名必须与类名一致。
构造函数没有返回值。
同一个类可以有多个参数列表不同的构造函数
当且仅当一个类没有声明构造函数时,编译器会自动生成一个无参无内容构造函数。
构造函数列表初始化
可以用列表初始化的方式对类成员直接赋值,这样做会提高程序运行效率。
类名::构造函数(参数1,参数2,...)
:类成员(参数1),类成员(参数2),...
{
}
注意:列表初始化对方式只支持将参数直接赋值给类成员。
构造函数隐式调用
当构造函数只有一个参数时,就可能出现构造函数隐式调用的现象。例如Number i = 55;这样的现象。
55是一个整数,i是一个Number类型,这却可以让整数初始化自己定义到类型。因为在这个过程中出现了构造函数隐式调用的情况
注意:在构造函数声明前加上explicit关键字可以关闭构造函数隐式调用的功能。
对象和对象的使用(三)
new和delet
new和delete是C++中的两个关键字,主要用于在向堆申请或者释放空间
和C语言不同的是,new/delete在申请/释放空间的时候还会调用构造函数和析构函数。
new的使用方法
使用new创建对象:
类名* 变量名 = new 构造函数(参数列表);
使用new创建对象数组:
类名* 变量名 = new 类名[数组的大小];//此时会调用多次构造函数
delete的使用方法
使用delete销毁对象:
delete 指向对象的指针;//此时会调用析构函数
使用delete销毁对象数组:
delete []对象数组名;//此时会调用多次析构函数
注意:delete和delete[]的选择。
this指针
当通过对象调用成员函数传递参数时,会额外将本身的地址做为参数传递进入函数。比如说我们实际上调用成员函数如下:
tom.introduce();
实际上编译器认为的代码是:
tom.introduce(&tom);
在函数体的内部可以通过this指针获取到编译器隐式传入的当前对象的地址,并访问对象的成员。例如:
this->类成员;
标准库类型string
string类型的介绍
标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。
建议在C++中使用string表示字符串,而不是使用C风格的字符串。
string类型的操作
使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。
#include <string>
using namespace std;
如何初始化类的对象是由类本身决定的,一个类可有定义很多种初始化对象的方式,只不过这些方式之间必须有所区别。
string s1;//空的字符串
string s2(s1);//初始化为s2为s1的副本
string s3 = s2;//初始化s3为s2的副本
string s4 = "value";//使用字符串常量
s.empty()//s为空返回true,否则返回false
s.size()//返回s中的字符个数
s[n]//返回s中第n个字符的引用,从0开始
s1 + s2//返回s1和s2连接后的结果
s1 = s2//用s2的副本代替s1中原来的字符
s1 == s2//判断字符串是否相等
cin>>s;//使用cin输入单个字符串
cin>>s1 >>s2;//使用cin输入多个字符串,以空白间隔
getline(cin,line);//使用getline读取一整行
封装的概念
封装的概念
面向对象有三大特性有封装,继承,多态。
将东西包装在一起,然后以新的完整形式呈现出来。
信息隐藏,隐藏对象的实现细节,不让外部直接访问到。
将数据和方法包装进类中,加上具体实现的隐藏,共同被称作封装,其结果是一个同时带有特征和行为的数据类型。
访问控制
在C++语言中,我们使用访问说明符加强类的封装性:
public:表示该C成员能在成员函数中直接访问,也能通过对象访问。
private:表示该成员能在成员函数中直接访问,但不能通过对象访问。
注意:class和struct都能用于表示类,但不同的是class默认的成员都是private的,而struct默认的成员则是public的
存取器
因为封装的缘故,类里面的成员变量一般会被定义为private,防止外部程序随意更改。
为了允许修改或读取对象的私有成员,就要提供公共的存储器函数。
封装的目的
实现信息的隐蔽
将使用者不需要知道的,或者不应该知道的数据处理流程隐蔽起来,只留下操作接口。
保护数据不被任意的使用与更改,欲修改数据都要通过特定的接口。
类的静态成员
静态成员的概念
在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。
使用静态成员实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。
静态成员的声明
静态成员变量的声明:
static 变量类型 变量名
静态成员函数的声明:
static 返回类型 函数名(参数列表);
静态成员的定义
静态成员变量的定义:
变量类型 类名::变量名 = 初始值;
静态成员函数的定义:
返回值类型 类名::函数名(参数列表)
{
//函数体
}
单例模式
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。
通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
函数高级特性
引用
引用的概念
引用(reference)为对象起了另一个名字,引用类型引用(refers to)另外一种类型。
引用非对象,对其进行的所有操作都是在与之绑定的对象上进行。
引用的注意事项
通过将声明符&来定义引用类型,而且引用必须绑定到特定的对象上,并且一经绑定无法解除。
引用类型 &引用名 = 特定的对象
引用只能绑定与引用类型相同的对象上。例如:int &r = 3.14;//错误的写法
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,其唯一的形参必须是引用。
编译器会自动生成拷贝构造函数,自动生成的拷贝构造函数会将参数中的所有非静态成员拷贝给当前对象
以下三种情况会调用拷贝函数:
对象作为函数参数,以值传递的方式传入函数体。
对象作为函数返回值,以值传递的方式从函数返回。
对象用于给另一个对象进行初始化。
使用引用的目的
可以在传递函数参数或返回函数返回值时,避免调用拷贝构造函数,提高程序执行效率。
关键字const
const引用
可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用。
与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:
const int ci = 1024;
const int &ri = ci;
常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未做限定,即const引用即可绑定到常量对象上也可绑定到非常量对象上。
尽可能在参数传递时使用const引用。
const成员函数
当成员函数不会更改对象的任何成员变量(static 除外)时,可以将成员函数声明为const:
class 类名
{
public:
返回类型 函数名(参数列表)const;
}
将const实施于成员函数的目的,是为了确定该成员函数可作用于const对象身上。
const_cast
对于将常量对象转换成非常量对象的行为,一般称其为“去掉const性质”。一旦去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。
如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const _cast执行写操作就会产生未定义的后果。
使用方法:
const char *pc;
char *p = const_cast<char*>(pc);
char &r = const_cast<char&>(*pc);
重载(一)
重载的概念
编程中重载是指函数名相同,函数的参数列表不同(包括参数个数和参数类型),至于返回类型可同可不同。
重载是可使函数、运算符等处理不同类型数据或接受不同个数的参数的一种方法。
函数的重载
如果同一个作用域内的几个函数名字相同但形参列表不同,称之为重载函数。
重载的函数接受的形参类型不一样,但是执行的操作非常类似。调用这些函数时,编译器会根据传递的实参类型推断想要的函数是哪个。
重载(二)
运算符重载的概念
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
重载的运算是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成。
运算符重载为函数
运算符重载为函数的参数数量应与该运算符的运算对象数量一样多,一元运算符有一个参数,二元有两个参数。
运算符重载为成员函数
如果要将运算符重载为成员函数,则它多一个(左侧)运算对象绑定到隐式的this指针上,因此,成员运算符函数的参数数量比运算符的运算对象少一个。
函数默认参数
默认参数的概念
函数的默认参数值,即在定义参数的时候同时给它一个初始值。在调用函数的时候,可以省略含有默认值的参数。
例如:
void func(int i=0,int j=1);
默认参数的注意事项
默认参数是严格按照从左至右的顺序执行。
默认参数的值只能在声明或定义中一处指定,不能同时指定。
默认参数和函数重载会出现二义性问题,如:
void func(); 和 void func(int i = 0);
友元
友元的概述
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。
友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享机制。通过友元,一个不同函数或另一个类中的成员函数可以访问类中的非公有成员。
友元函数
友元函数是能够访问类中的私有成员的非成员函数。从语法上看,它与普通函数的声明、定义和调用一样,只是友元函数需要在类中额外声明,并在类中的声明加上friend关键字。
友元函数的声明:
friend 函数返回值 函数名(参数列表);
友元类
友元类是能够通过成员函数访问其他类中的私有成员的类。从语法上看,它与普通类一样,只是友元类需要在访问私有成员的类中额外声明,并在声明前加上friend关键字。
友元类的声明:
friend class 类名;
内联
内联函数的介绍
内联函数是指用inline关键字修饰的成员函数或非成员函数。
内联函数从源代码层看,它具有函数的结构,而在编译后,却不具有函数的性质。编译时,类似宏替换,使用函数体替换调用处的函数名。
内联函数的使用
在函数的声明前加上关键字inline即可:
inline 返回值 函数名(参数列表);
内联函数的注意事项
内联函数中不允许使用循环语句、switch语句、递归调用。
内联函数只适用于1~5行的小函数。
在类结构内部定义的函数默认为内联函数。
内联函数的声明定义尽可能写在头文件中。
inline关键字只是向编译器提出内联的请求,编译器有可能忽略这个请求。
面向对象编程高级
继承(一)
继承的概念
继承是类与类之间的一种关系。如果类A继承于类B,则把类B称为基类(父类),类A称为派生类(子类)。
继承需要符合is-a的关系,基类更通用更抽象,派生类更特殊更具体。
比如说动物是一个类,食肉动物也是一个类。而动物有的特性食肉动物都有,也就是说食肉动物是动物(is-a),即食肉动物继承于动物。同理,狮子看作一个类时,它也应当继承于食肉动物。
继承的目的
提高代码的重用性。因为派生类会拥有基类的所有特性,所以当写派生类的时候就没有必要把基类相同功能的代码再写一遍。
提高代码的可拓展性。派生类可以在基类的基础上添加新的代码,而且一个基类可以出现多个派生类。
简单的继承
公有继承的子类声明:
class派生类名:public 基类名
{
//成员的声明
};
继承(二)
对象的构造和析构过程
任何对象在创建后都会调用构造函数,在销毁之前都会调用析构函数。
因为派生类对象除了本身成员之外还拥有基类的成员,所以派生类对象在创建时会调用父类的构造函数,销毁时调用父类的析构函数。
派生类的构造函数
在定义派生类的构造函数时需要知道父类的构造函数,如果不指定则会调用父类的无参构造函数。
派生类构造函数的定义:
派生类名::派生类名(参数列表)
:基类名(基类构造函数参数列表),派生类成员(参数),...
{
}
访问控制符
private:只能在当前类成员函数能够访问。
protected:只能在当前类成员函数和子类成员函数中能够访问。
public:即能在当前类成员函数,子类成员函数中访问,也能通过当前类对象,子类对象访问。
继承(三)
受保护继承和私有继承
在C++中最常见的是公有继承,除了公有继承外,还有受保护的继承和私有继承。
在受保护继承中,派生类从基类继承下来的私有成员会隐蔽,所有非私有成员都会变成受保护成员。
在私有继承中,派生类从基类继承下来的私有成员会隐蔽,所有非私有成员都会变成私有成员。
受保护继承的语法
受保护继承的派生类声明和公有继承类似:
class 派生类名:protected 基类名
{
//成员
};
私有继承的语法
私有继承的派生类声明和公有继承类似:
class 派生类名:private 基类名
{
//成员
};
多重继承
在C++中,一个派生类可以继承多个基类
如下:
class 派生类:public 基类A,public 基类B,...
{
//成员
}
虚拟继承
虚拟继承是多重继承中特有的概念,是为了解决多重继承出现的一些问题。
比如说,类D继承自类B1、B2,而类B1、B2都继承自类A,则此时类D中会出现两次类A中的成员。为了节省内存,可以将B1、B2对A的继承定义为虚拟继承。
虚拟继承
虚拟继承的语法:
class 派生类:virtual public 基类
{
//成员
};
注意:public和virutal的位置可以互换
虚函数
虚函数的概念
虚函数是指在类成员函数声明前加上virtual关键字的函数。
当基类的成员函数无法满足子类要求的时候,我们可以将该成员函数声明为虚函数以供子类改写,虚函数的声明方式如下:gon
class 基类名
{
virtual 返回类型 函数名(参数列表);
};
虚函数列表
当一个类中出现了虚函数时,编译器会为这个类的对象多分配4个字节的空间用于存放虚函数列表。
在虚函数列表中存放了所有虚函数的函数指针。
当对象调用虚函数时,会优先从虚函数列表中寻找匹配的函数指针进行调用。
函数重写
派生类可以对基类的虚函数进行重新声明定义,这种行为称之为函数重写。
函数重写会将派生类对象继承自基类的虚函数列表部分进行重写。
纯虚函数和抽象类
纯虚函数的概念
纯虚函数是只需要声明,不需要定义的虚函数。声明方法如下:
class 类名
{
virtual 返回类型 函数名(参数列表)= 0;
};
抽象类的概念
有纯虚函数的类被称为抽象类。
抽象类无法通过构造函数直接生成对象,只能够被派生类继承。
当抽象类被继承时,该类的所有纯虚函数必须在子类中重写,否则子类也将称为一个抽象类。
多态
多态的概念
在面向对象语言中,接口的多种不同实现方式即为多态。
多态能允许将父对象设置成一个或更多个它的子对象的技术,赋值之后父对象就可以根据当前赋值给它的子对象的特性进行运作。
可以简单的理解为:将基类对象的指针或引用绑定到派生类对象上,此时基类的指针或引用就可以拥有派生类的特性。
函数重写实现多态
实现多态的步骤:
第一步,派生类重写基类的虚函数。
第二步,基类的指针或引用绑定到派生类的对象上。
第二步,通过基类的指针或引用调用所绑定派生类重写过的函数。
泛型编程
函数模板
函数模板的概念
函数模板是用于生成函数的模板。在编译阶段,编译器会根据函数模板的使用情况创建出函数名相同,参数类型由用户指定的若干函数。
通过函数模板创建的函数拥有相同的函数体,只是函数的参数类型不同。
函数模板的使用
每当在一个编译单元(经过预处理的.cpp文件)中使用了函数模板,则必须在该编译单元中给出函数模板的定义。
因此,建议在头文件中对函数模板进行声明定义。
函数模板的声明:
template<typename T>
返回类型 函数名(参数列表);
其中T表示任意类型,参数的类型和返回类型都可以指定为T
函数模板的定义:
template<typename T,...>
返回值类型 函数名(参数列表)
{
//函数体
}
函数模板的特化
函数模板特化是指在实例化函数模板时,对特定类型的实参进行特殊的处理,即当实参为特定类型时,通过函数模板生成的函数会有不同的函数体。
特化时需要为函数模板添加新的定义,方式如下:
template<>
返回类型 函数名<特定的参数类型>(参数列表)
{
//函数体
}
类模板
类模板的概念
类模板时用于生成类的模板。在编译阶段,编译器会根据类模板的使用情况创建出仅成员数据类型,和成员函数的参数类型不同,其他完全相同的若干类。
类模板的使用
类模板的声明如下,其中T表示任何类型,由用户指定:
template<typename T,...>
class 类名
{
//成员
};
类模板的成员函数定义如下,一般类模板的定义也应该写在头文件中:
template<typename T,...>
返回类型 类名<T,...>::成员函数名(形参列表)
{
//函数体
}
类模板的特化
类模板特化是指在实例化类模板时,对特定类型的泛型进行特殊处理,即用户指定所有特定类型的类模板时,通过类模板生成的成员函数会有不同的函数体。
特化类模板时需要对整个类模板进行声明定义:
template<>
class 类名<指定类型,指定类型>
{
//类成员
};
类模板的偏特化
偏特化与特化类似,只是特化会指定所有的泛型,而偏特化只指定部分泛型。
偏特化类模板时需要对整个类模板进行声明定义:
template<>
class 类名<指定类型,...,泛型>
{
//类成员
};
顺序容器
容器的概念
容器是用于存储相同数据类型的类模板,它既可以存储预定义的数据类型,也可以存储用户之定义的数据类型。
C++标准模板库(STL)提供了10中容器类型用于存储数据。这10种容器用于存储数据的数据结构各不相同,比如我们熟知的数组,链表。
容器可以根据数据结构分成顺序容器和关联容器。
顺序容器的概念
顺序容器是用于存放多个单一类型数据的类模板。
顺序容器是通过数据在容器中的位置来对数据进行访问。
顺序容器的种类与选择
vector:数组,支持快速随机访问
list:链表,支持快速插入删除
队列,支持对首位元素的快速增删,和随机访问
vector的使用
vector的概念
vector是基于数组实现的顺序容器。
可以把vector看作存储任意相同类型数据,容器可变大小的数据。
vector的操作
使用vector时需要包含头文件以及使用命名空间std。
可以通过线上C++帮助文档获取vector的各个成员函数的介绍及使用方法。网址:www.cplusplus.com
迭代器
迭代器的概念
迭代器是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定地址。
迭代器提供了一些类似于指针的运算符:* 、++、 ==、 !=、 =。这些操作和C/C++操作指针的接口一致。简而言之,迭代器虽然不是指针,但是我们可以把它看作指向容器中元素的指针。
迭代器常用操作
获取不同容器的迭代器类型:
模板类类型::iterator
获取容器中的指向首个元素的迭代器:
模板类对象.begin();
获取容器中的尾后迭代器:
模板类对象.end();
度迭代器对象可以进行类似于指针的操作,比如运算符*、 +=、 ++ 、=等。
迭代器失效的概念
因为容器存储数据的容量会发生变化,所以当对容器内元素进行增删时迭代器可能出现失效的情况。
比如说数组中原先有5个元素,删除其中的第3个元素,则第4个和第5个元素的迭代器(在内存中的地址)就会发生变化,那么如果有指向这两个元素的迭代器在删除第三个元素时就会失效。
关联容器
关联容器的概念
关联容器是用于存放多个单一类型数据的类模板。
关联容器是内部存储元素的形式大多是以哈希表或二叉树等非线性的数据结构进行存储。
关联容器一般而言增删元素时效率较低,但是查找的速度会比线性表更快。
关联容器的种类与选择
set 容器中存储的元素作为键,键不能重复,能够快速读取。
map 容器中除了键外,每个键还有对应的值。键不能重复,值可以重复。
multiset 支持一个键多次出现的set类型。
multimap 支持一个键多次出现的map类型。
map
pair的概念
pair 是有两个泛型的模板类,原型声明如下:
template<class T1,class T2>struct pair;
它有两个成员变量first(T1类型)和second(T2类型),分别表示键和键所对应的值。
map的概念
map是关联容器的一种,该容器存放元素的类型是pair。其中pair的键不能重复。
由于map的底层实现是将键按照大小存入二叉树中,以便于查询。所以存入map中的键的类型必须能够比较大小(重载<运算符或者提供比较函数指针)
map的使用
使用vector时需要包含头文件
set
set的概念
是关联容器的一种,主要用于快速的到元素进行查找。
由于set的底层实现是将键按照大小存入二叉树中,以便于查询。所以存入set中的元素的类型必须能够比较大小(重载<运算符或者提供比较函数指针)。
set的使用
使用set时需要包含头文件以及使用命名空间std。
可以通过线上C++帮助文档获取set的各个成员函数的介绍及使用方法。