STL(标准模板库),是目前C++内置支持的library。它的底层利用了C++类模板和函数模板的机制,由三大部分组成:容器、算法和迭代器。前端
vector就是动态数组。在堆中分配内存,元素连续存放,有保留内存,若是减小大小后,内存也不会释放。ios
若是新值>当前大小时才会再分配内存。c++
它拥有一段连续的内存空间,而且起始地址不变,所以它能很是好的支持随即存取,即[]操做符,但因为它的内存空间是连续的,因此在中间进行插入和删除会形成内存块的拷贝。算法
当该数组后的内存空间不够时,须要从新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。底层数据结构为数组 ,支持快速随机访问。swift
【vector总结】数组
须要常常随机访问请用vector缓存
【vector的基本用法】bash
front()返回头部元素的引用,能够当左值数据结构
back()返回尾部元素的引用,能够当左值less
push_back()添加元素,只能尾部添加
pop_back()移除元素,只能在尾部移除
int main(int argc, const char * argv[]) {
//定义一个vector容器
vector<int> v1;
//插入元素(尾部插入)
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
//迭代器遍历打印
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//修改头部元素的值(front()返回是引用,能够当左值)
v1.front() = 44;
//输出头部元素
cout<< "头部元素:" << v1.front() << endl;
//修改尾部的值(back()返回是引用,能够当左值)
v1.back() = 99;
//输出尾部元素
cout << "尾部元素" << v1.back() <<endl;
//删除元素(从尾部删除)
v1.pop_back();
//迭代器遍历打印
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
复制代码
【vector的初始化】
vector有4种方式初始化,有直接初始化,也有经过拷贝构造函数初始化。
int main(int argc, const char * argv[]) {
//直接构造函数初始化
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
//经过拷贝构造函数初始化
vector<int> v2 = v1;
//使用部分元素来构造
vector<int> v3(v1.begin(), v1.begin() + 1);
vector<int> v4(v1.begin(), v1.end());
//存放三个元素,每一个元素都是9
vector<int> v5(3,9);
return 0;
}
复制代码
【vector的遍历】
vector的遍历有多种方式,能够根据[]或者迭代器遍历。
[]方式,若是越界或出现其余错误,不会抛出异常,可能会崩溃,可能数据随机出现
at方式,若是越界或出现其余错误,会抛出异常,须要捕获异常并处理
迭代器提供了逆向遍历,能够经过迭代器来实现逆向遍历,固然上面两种方式也能够
int main(int argc, const char * argv[]) {
//建立vector
vector<int> v1;
//插入元素
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
//遍历-[]取值
for (int i = 0; i < v1.size(); i++) {
cout << v1[i] << " ";
}
cout << endl;
//遍历-at取值
for (int i = 0; i < v1.size(); i++) {
cout << v1.at(i) << " ";
}
cout << endl;
//遍历-迭代器遍历
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//遍历-迭代器逆向遍历
for (vector<int>::reverse_iterator it = v1.rbegin(); it != v1.rend(); it++) {
cout << *it << " ";
}
cout << endl;
//测试越界
cout << "[]越界:" << v1[20] << endl; //不会抛出异常,可能会崩溃,可能会乱码
cout << "at越界:" << v1.at(20) << endl; //会抛出异常,须要捕获异常
return 0;
}
复制代码
【vector的push_back强化】
push_back是在当前vector的内存末尾拷贝元素进入容器。注意这个地方可能产生浅拷贝,因此容器中的对象要支持拷贝操做。另外,若是vector初始化了个数,而不初始化具体的值,push_back也只会在最后面追加。
int main(int argc, const char * argv[]) {
//初始化10个元素的容器
vector<int> v(10);
//打印容器大小
cout << v.size() << endl;
//push_back添加元素
v.push_back(100);
//打印容器大小
cout << v.size() << endl;
//遍历后的结果是 0 0 0 0 0 0 0 0 0 0 100
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
复制代码
【vector的元素删除】
vector的删除,是根据位置进行删除,若是想要删除某个元素,须要找到当前元素的迭代器位置,再进行删除。
erase(iterator)函数,删除后会返回当前迭代器的下一个位置。
int main(int argc, const char * argv[]) {
//1 建立容器并初始化
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++) {
v1[i] = i;
}
//2 区间删除
//--2.1 删除前3个元素
v1.erase(v1.begin(), v1.begin() + 3);
//--2.2 删除指定位置的元素
v1.erase(v1.begin() +3);
//3 根据元素的值进行删除,删除值为2的元素
v1.push_back(2);
v1.push_back(2);
vector<int>::iterator it = v1.begin();
while (it != v1.end()) {
if (*it == 2) {
it = v1.erase(it); //删除后,迭代器指针会执行下一个位置并返回。
}else{
it++;
}
}
//4 遍历打印
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
复制代码
【vector的插入元素】
vector提供了insert函数,结合迭代器位置插入指定的元素。若是迭代器位置越界,会抛出异常。
int main(int argc, const char * argv[]) {
//初始化vector对象
vector<int> v1(10);
//在指定的位置插入元素10的拷贝
v1.insert(v1.begin() + 3, 10);
//在指定的位置插入3个元素11的拷贝
v1.insert(v1.begin(), 3, 11);
//遍历
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
复制代码
list就是双向链表,在堆中存放,每一个元素都是放在一块内存中,它的内存空间能够是不连续的,经过指针来进行数据的访问,这个特色使得它的随机存取变的很是没有效率,所以它没有提供[]操做符的重载。
但它能够以很好的效率支持任意地方的删除和插入。
list没有空间预留习惯,因此每分配一个元素都会从内存中分配,每删除一个元素都会释放它占用的内存.底层数据结构为双向链表,支持快速增删
【list总结】
若是你喜欢常常添加删除大对象的话,那么请使用list 要保存的对象不大,构造与析构操做不复杂,那么可使用vector代替 list<指针>彻底是性能最低的作法,这种状况下仍是使用vector<指针>好,由于指针没有构造与析构,也不占用很大内存
【list的基本操做】
int main(int argc, const char * argv[]) {
//建立list对象
list<int> l;
//尾部添加元素
for (int i = 0; i < 10; i++) {
l.push_back(i);
}
//头部添加元素
l.push_front(111);
//遍历
for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
//list不能随机访问
list<int>::iterator it = l.begin();
it++;
it++;
cout << *it <<endl;
// it = it + 1; //编译报错,不能随机访问
// it = it + 5; //编译报错,不能随机访问
return 0;
}
复制代码
【list的删除】
list提供了两个函数用来删除元素,分别是erase和remove。
erase是经过位置或者区间来删除,主要结合迭代器指针来操做
remove是经过值来删除
int main(int argc, const char * argv[]) {
//建立list对象
list<int> l;
//添加数据
for (int i = 0; i < 10; i++) {
l.push_back(i);
}
l.push_back(100);
l.push_back(100);
//删除头部元素
l.erase(l.begin());
//删除某个区间
list<int>::iterator it = l.begin();
it++;
it++;
it++;
l.erase(l.begin(), it);
//移除值为100的全部元素
l.remove(100);
//遍历
for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
复制代码
[堆1] --> [堆2] -->[堆3] -->...
每一个堆保存好几个元素,而后堆和堆之间有指针指向,看起来像是list和vector的结合品.
它支持[]操做符,也就是支持随机存取,可让你在前面快速地添加删除元素,或是在后面快速地添加删除元素,而后还能够有比较高的随机访问速度,和vector的效率相差无几。
它支持在两端的操做:push_back,push_front,pop_back,pop_front等,而且在两端操做上与list的效率也差很少。
在标准库中vector和deque提供几乎相同的接口,在结构上它们的区别主要在于这两种容器在组织内存上不同,deque是按页或块来分配存储器的,每页包含固定数目的元素.相反vector分配一段连续的内存,vector只是在序列的尾段插入元素时才有效率,而deque的分页组织方式即便在容器的前端也能够提供常数时间的insert和erase操做,并且在体积增加方面也比vector更具备效率
【deque操做】
int main(int argc, const char * argv[]) {
//定义deque对象
deque<int> d1;
//尾部插入元素
d1.push_back(10);
d1.push_back(20);
d1.push_back(30);
//头部插入元素
d1.push_front(1);
d1.push_front(2);
d1.push_front(3);
//尾部删除元素
d1.pop_back();
//头部删除元素
d1.pop_front();
//修改头部和尾部的值
d1.front() = 111;
d1.back() = 222;
//查找元素为1的下标
//经过distance求取下标
deque<int>::iterator it = d1.begin();
while (it != d1.end()) {
if (*it == 1) {
cout << "下标:" << distance(d1.begin(), it) << endl;
}
it++;
}
//遍历
for (deque<int>::iterator it = d1.begin(); it != d1.end(); it++) {
cout << *it << " ";
}
cout << endl;
return 0;
}
复制代码
【vector、list、deque总结】
vector是能够快速地在最后添加删除元素,并能够快速地访问任意元素
list是能够快速地在全部地方添加删除元素,可是只能快速地访问最开始与最后的元素
deque在开始和最后添加元素都同样快,并提供了随机访问方法,像vector同样使用[]访问任意元素,可是随机访问速度比不上vector快,由于它要内部处理堆跳转 deque也有保留空间.另外,因为deque不要求连续空间,因此能够保存的元素比vector更大,这点也要注意一下.还有就是在前面和后面添加元素时都不须要移动其它块的元素,因此性能也很高。
所以在实际使用时,如何选择这三个容器中哪个,应根据你的须要而定,通常应遵循下面的原则:
一、若是你须要高效的随即存取,而不在意插入和删除的效率,使用vector
二、若是你须要大量的插入和删除,而不关心随即存取,则应使用list
三、若是你须要随即存取,并且关心两端数据的插入和删除,则应使用deque。
底层通常用list或deque实现,封闭头部便可,不用vector的缘由应该是容量大小有限制,扩容耗时
▽ 基础数据类型的stack
int main(int argc, const char * argv[]) {
//定义stack对象
stack<int> s1;
//入栈
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
//打印栈顶元素,并出栈
while (!s1.empty()) {
//取出栈顶元素
cout << "当前栈顶元素" << s1.top() << endl;
//获取栈的大小
cout << "当前栈的大小" << s1.size() << endl;
//出栈
s1.pop();
}
return 0;
}
复制代码
▽ 复杂数据类型的stack
//定义类
class Teacher {
public:
char name[32];
int age;
void printT() {
cout << "age = " << age << endl;
}
};
int main(int argc, const char * argv[]) {
Teacher t1, t2, t3;
t1.age = 22;
t2.age = 33;
t3.age = 44;
//定义栈容器
stack<Teacher> s1;
//入栈
s1.push(t1);
s1.push(t2);
s1.push(t3);
//出栈并打印
while (!s1.empty()) {
//打印栈顶元素
Teacher tmp = s1.top();
tmp.printT();
//出栈
s1.pop();
}
return 0;
}
复制代码
底层通常用list或deque实现,封闭头部便可,不用vector的缘由应该是容量大小有限制,扩容耗时 (stack和queue实际上是适配器,而不叫容器,由于是对容器的再封装)
#include <queue>
void main() {
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout << "对头元素" << q.front() <<endl;
cout << "队列的大小 " << q.size() <<endl;
while (!q.empty()) {
int tmp = q.front();
cout << tmp << " ";
q.pop();
}
}
class Teacher {
public:
int age;
char name[32];
void printT() {
cout << "age :" << age <<endl;
}
}
void main() {
Teacher t1,t2,t3;
t1.age = 31;
t2.age = 32;
t3.age = 33;
queue<Teacher > q;
q.push(t1);
q.push(t2);
q.push(t3);
while (!q.empty()) {
Teacher tmp = q.front();
tmp.printT();
q.pop();
}
}
复制代码
底层数据结构通常为vector为底层容器,堆heap为处理规则来管理底层容器实现
优先级队列分为:最小值优先队列和最大值优先队列。
此处的最大值、最小值是指队头的元素(增序、降序)。默认,是建立最大值优先级队列。 定义优先级的方法:
priority_queue默认定义int类型的最大值队列
priority_queue<int, vector, less>定义int型的最大值优先队列
priority_queue<int, vector, greater>定义int型的最小值队列
上面的定义中,less和greater至关于谓词,是预约义好的排序函数,称之为“仿函数”。
void main() {
//定义优先级队列(默认是最大值优先级队列)
priority_queue<int> p1;
//定义一个最大优先级队列
//less是提早定义好的预约义函数 至关于谓词
priority_queue<int, vector<int>, less<int>> p2;
//定义一个最小值优先级队列v
priority_queue<int, vector<int>, greater<int>> p3;
//给默认的最大优先级队列入栈
p1.push(33);
p1.push(11);
p1.push(55);
p1.push(22);
//打印最大优先级的对头元素
cout<<"对头元素:"<< p1.top() <<endl;
cout<<"队列的大小:"<< p1.size() <<endl;
//给最小值优先级队列入栈
p3.push(33);
p3.push(11);
p3.push(55);
p3.push(22);
//打印最小优先级队列的对头元素
cout<<"对头元素:"<< p3.top() <<endl;
cout<<"队列的大小:"<< p3.size() <<endl;
}
复制代码
集合, 用来判断某一个元素是否是在一个组里面,使用的比较少,底层数据结构为红黑树,有序,不重复
【set元素的插入和删除】
set提供了insert和erase函数,用来对元素进行插入和删除操做。
基础类型数据,若是插入的是重复的元素,则插入失败,返回值是一个pair类型(pair类型相似于swift语言中的元组的概念,经过其判断是否插入成功)
复杂类型数据,须要经过仿函数来肯定元素的顺序,判断是不是重复元素。
void main() {
set<int> set1;
//插入元素
for (int i = 0; i<5; i++) {
int tmp = rand();
set1.insert(tmp);
}
//重复插入元素(会插入不成功,下一节会分析若是根据返回值判断是否插入成功)
set1.insert(100);
set1.insert(100);
set1.insert(100);
set1.insert(100);
for (set<int>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << *it <<" ";
}
//删除集合
while(!set1.empty())
{
//获取头部
set<int>::iterator it = set1.begin();
//打印头部元素
cout << *it << endl;
//从头部删除元素
set1.erase(set1.begin());
}
}
复制代码
【普通数据类型的排序】
set容器是有序的集合,默认的顺序是从小到大的。建立集合的方式:
set建立默认的从小到大的int类型的集合
set<int,less>建立一个从小到大的int类型的集合
set<int,greater>建立一个从大到小的int类型的集合
上面的less和greater就是仿函数,集合会根据这个仿函数的返回值是否为真类进行排序。
//仿函数的原型,下面是C++提供的默认的greater的仿函数(删除了宏定义后的)
struct greater {
bool operator()(const int &left, const int &right) const {
//若是左值>右值,为真。从大到小的排列
return left > right;
}
};
复制代码
添加进set集合的元素确实是有序的。
void main() {
//默认,从小到大
set<int> set1;
//从小到大--默认就是
set<int, less<int>> set2;
//从大到小
set<int, greater<int>> set3;
//添加元素
for (int i = 0; i < 5; i++) {
int tmp = rand();
set3.insert(tmp);
}
//遍历
for (set<int>::iterator it = set3.begin(); it != set3.end(); it++) {
cout<< *it << " ";
}
}
复制代码
【自定义对象的排序】
基础数据类型的set是有序的关键缘由是greater和less仿函数。
自定义对象的有序是经过自定义仿函数来实现。
仿函数,之因此叫仿函数,是由于它跟函数很像,但并非一个函数。它的结果以下,只要咱们实现了这个仿函数,咱们也能够对自定义对象进行排序。
//定义仿函数的结构体
struct FunctionName {
//重载了()运算符,实现两个自定义对象的比较
bool opeartor() (Type &left, Type &right) {
//左值大于右值,从大到小的顺序
if(left > right)
return true;
else
return false;
}
};
复制代码
下面,咱们自定义一个Student对象,根据年龄进行排序,将对象加入到set集合中,并进行打印。
//定义student对象
class Student {
public:
Student(const char *name, int age)
{
strcpy(this->name, name);
this->age = age;
}
public:
char name[64];
int age;
};
//提供仿函数,用于自定义对象的set进行排序,要写一个仿函数,用来排序
struct FuncStudent {
//重载了括号操做符,用来比较大小
bool operator() (const Student &left, const Student &right) {
//若是左边比右边小,从小到大按照年龄排序
if(left.age < right.age)
return true;
else
return false;
}
};
void main() {
Student s1("s1",32);
Student s2("s2",22);
Student s3("s3",44);
Student s4("s4",11);
Student s5("s5",22);
//建立集合,采用从小到大的排序
set<Student, FuncStudent> set1;
//插入数据
set1.insert(s1);
set1.insert(s2);
set1.insert(s3);
set1.insert(s4);
//插入不进去(年龄有重复的,因此插不进去了),要经过返回值来确保是否插入成功
set1.insert(s5);
//遍历
for (set<Student>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << it->age << "\t" << it->name <<endl;
}
}
复制代码
【pair类型的返回值】
pair类型,就相似于Swift语言中的“元组”的概念,这个类型包含了多个数据类型,在函数返回的时候,能够同时返回多个值。
它其实是一个结构体。它包含了两个属性,first和second。
template <class _T1, class _T2> struct pair {
typedef _T1 first_type;
typedef _T2 second_type;
_T1 first;
_T2 second;
}
复制代码
set集合中的元素是惟一的,重复的元素插入会失败。
判断是否插入成功,咱们能够经过insert函数的返回值来判断,它的返回值是一个pair类型。
insert函数的原型: pair<iterator,bool> insert(const value_type& __v)
返回的是pair<iterator, bool>类型,pair的第一个属性表示当前插入的迭代器的位置,第二个属性表示插入是否成功的bool值。咱们能够经过第二个属性来判断元素是否插入成功。
//pair的使用判断set的insert函数的返回值
void test3() {
Student s1("s1",32);
Student s2("s2",22);
Student s3("s3",44);
Student s4("s4",11);
Student s5("s5",22);
//建立集合,采用从小到大的排序
set<Student, FuncStudent> set1;
//插入数据,接收返回值
pair<set<Student, FuncStudent>::iterator, bool> pair1 = set1.insert(s1);
if (pair1.second == true) {
cout << "插入s1成功" <<endl;
}else{
cout << "插入s1失败" <<endl;
}
复制代码
【set查找元素】
set容器提供了多个函数用来查找元素
iterator find(const key_type& __k) find函数查找元素为k的迭代器位置
iterator lower_bound(const key_type& __k) lower_bound函数查找小于等于元素k的迭代器位置
iterator upper_bound(const key_type& __k) upper_bound函数查找大于元素k的迭代器位置
pair<iterator,iterator> equal_range(const key_type& __k) equal_range函数返回一个pair类型,第一个属性表示大于等于k的迭代器位置,第二个是大于k的迭代器位置
void test4() {
set<int> set1;
for (int i = 0; i < 10; i++) {
set1.insert(i+1);
}
//遍历
for (set<int>::iterator it = set1.begin(); it != set1.end(); it++) {
cout << *it <<endl;
}
//查找元素是5的迭代器位置
set<int>::iterator it0 = set1.find(5);
cout << "it0:" << *it0 <<endl;
//查找小于等于5的元素迭代器位置
set<int>::iterator it1 = set1.lower_bound(5);
cout << "it1:" << *it1 <<endl;
//查找大于5的元素迭代器位置
set<int>::iterator it2 = set1.upper_bound(5);
cout << "it2:" << *it2 <<endl;
//返回的pair第一个迭代器是>=5,另外一个是>5
pair<set<int>::iterator, set<int>::iterator> mypair = set1.equal_range(5);
set<int>::iterator it3 = mypair.first;
set<int>::iterator it4 = mypair.second;
cout << "it3:" << *it3 <<endl;
cout << "it4:" << *it4 <<endl;
}
复制代码
底层数据结构为红黑树,有序,可重复
multiset容器,与set容器类似,可是multiset容器中的元素能够重复。另外,他也是自动排序的,容器内部的值不能随便修改,由于有顺序的。
void test5() {
//定义multiset
multiset<int> set1;
//从键盘不停的接收值
int tmp = 0;
printf("请输入multiset集合的值:");
scanf("%d", &tmp);
while (tmp != 0) {
set1.insert(tmp);
scanf("%d", &tmp);
}
//迭代器遍历
for (multiset<int>::iterator it = set1.begin(); it != set1.end(); it++) {
cout<< *it <<" ";
}
cout <<endl;
//删除
while (!set1.empty()) {
multiset<int>::iterator it = set1.begin();
cout << *it << " ";
set1.erase(it);
}
}
复制代码
映射,至关于字典,把一个值映射成另外一个值,若是想建立字典的话使用它。底层采用的是树型结构,多数使用平衡二叉树实现,查找某一值是常数时间,遍历起来效果也不错,只是每次插入值的时候,会从新构成底层的平衡二叉树,效率有必定影响。底层数据结构为红黑树,有序,不重复
【map元素的插入与删除】
int main() {
map<int, string> map1;
//insert方法插入
//--1 经过pair<int, string>(1,”chenhua“) 构造pair元素
map1.insert(pair<int, string>(1,"chenhua"));
//--2 经过make_pair构造pair元素
map1.insert(make_pair(2,"mengna"));
//--3 经过value_type构造pair元素
map1.insert(map<int, string>::value_type(3,"chenmeng"));
//[]直接插入
map1[4] = "menghua";
//重复插入(插入会不成功)
pair<map<int, string>::iterator, bool> pair1 = map1.insert(make_pair(2, "haha"));
if (pair1.second) {
cout << "重复插入成功" << endl;
}else{
cout << "重复插入失败" << endl;
}
//元素的修改
//map[1] = "22"的方式,若是不存在键则插入,存在键则修改
map1[2] = "haha";
//元素的删除
//--删除值为"haha"的元素
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++) {
if (it->second.compare("haha") == 0) {
map1.erase(it);
}
}
//遍历
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++) {
cout << it->first << "\t" << it->second << endl;
}
return 0;
}
复制代码
【map元素的查找】
map提供了两个函数进行key的查找:find和equal_range。
int main() {
//定义map
map<int ,string> map1;
map1[1] = "chenhua";
map1[2] = "mengna";
//查找key=100的键值对
map<int, string>::iterator it = map1.find(100);
if (it != map1.end()) {
cout << "存在key=100的键值对";
}else{
cout << "不存在" << endl;
}
//查找key = 1000的位置
//返回两个迭代器,第一个表示<=1000的迭代器位置,第二个是>1000的迭代器位置
pair<map<int, string>::iterator, map<int, string>::iterator> mypair = map1.equal_range(1000);
if (mypair.first == map1.end()) {
cout << "大于等于5的位置不存在" << endl;
}else{
cout << mypair.first->first << "\t" << mypair.first->second << endl;
}
if (mypair.second == map1.end()) {
cout << "大于5的位置不存在" << endl;
}else{
cout << mypair.second->first << "\t" << mypair.second->second << endl;
}
return 0;
}
复制代码
底层数据结构为红黑树,有序,可重复
multimap容器,与map容器的惟一区别是:multimap支持多个键值。
因为支持多个键值,multimap提供了cout函数来计算同一个key的元素个数。
class Person {
public:
string name; //姓名
int age; //年龄
string tel; //电话
double sal; //工资
};
void test() {
Person p1,p2,p3,p4,p5;
p1.name = "王1";
p1.age = 31;
p2.name = "王2";
p2.age = 31;
p3.name = "张3";
p3.age = 31;
p4.name = "张4";
p4.age = 31;
p5.name = "钱5";
p5.age = 31;
multimap<string, Person> map2;
//sale部门
map2.insert(make_pair("sale", p1));
map2.insert(make_pair("sale", p2));
//development部门
map2.insert(make_pair("development", p3));
map2.insert(make_pair("development", p4));
//Finanncial部门
map2.insert(make_pair("Finanncial", p5));
//遍历
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++) {
cout << it->first << "\t" << it->second.name << endl;
}
//按部门显示员工信息
int developNum = (int) map2.count("development");
cout << "development部门人数:" << developNum << endl;
multimap<string,Person>::iterator it2 = map2.find("development");
int tag = 0;
while (it2 != map2.end() && tag < developNum) {
cout << it2->first << "\t" << it2->second.name <<endl;
it2 ++;
tag ++;
}
//把age=32 修改name= 32
for (multimap<string, Person>::iterator it = map2.begin(); it != map2.end(); it++) {
if (it->second.age == 32) {
it->second.name = "32";
}
}
}
int main(int argc, const char * argv[]) {
test();
return 0;
}
复制代码
底层数据结构为hash表,无序,不重复
底层数据结构为hash表,无序,可重复
底层数据结构为hash表,无序,不重复
底层数据结构为hash表,无序,可重复
STL的容器主要利用了C++的模板特性来实现。须要注意:
针对容器,容器之间也支持拷贝。因此须要注意:
【STL容器的元素拷贝】 若是容器元素若是没有实现拷贝构造函数,出现浅拷贝后的崩溃问题。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Student {
public:
Student(const char *name, int age)
{
cout << "构造函数" << endl;
//分配内存空间
m_name = new char[strlen(name) + 1];
//值拷贝
strcpy(m_name, name);
m_age = age;
}
~Student()
{
printf("%p 指向的空间 调用析构函数\n", m_name);
if (m_name != NULL) {
delete []m_name;
m_age = 0;
}
}
private:
char *m_name;
int m_age;
};
int main() {
Student s1("chenhua",24);
vector<Student> v1;
v1.push_back(s1);
return 0;
}
复制代码
上面的代码段,运行后的结果以下:
构造函数
0x100302a00 指向的空间 调用析构函数
0x100302a00 指向的空间 调用析构函数
复制代码
运行后,打印出结果后并报错。报错缘由是同一个内存空间被释放了2次,致使的崩溃。
其根本缘由是,v1将s1拷贝到容器,因为Student没有重写拷贝构造函数,从而出现了浅拷贝,只拷贝了地址。释放的时候毫无疑问出现错误。 若是咱们给Student重写了拷贝构造函数和重载了等号操做符,则上面的错误就不会出现。
//重写拷贝构造函数
Student(const Student &obj)
{
//分配内存空间
m_name = new char[strlen(obj.m_name) + 1];
//值拷贝
strcpy(m_name, obj.m_name);
m_age = obj.m_age;
}
//重载等号运算符
Student & operator=(const Student &obj)
{
//释放旧值
if (m_name != NULL) {
delete [] m_name;
m_age = 0;
}
//分配内存空间并赋值
m_name = new char[strlen(obj.m_name) + 1];
strcpy(m_name, obj.m_name);
m_age = obj.m_age;
return *this;
}
复制代码