c++第三部分
黑马程序员课程学习笔记
模板
template<class T>
//接着写的是类,就叫类模板,如果是函数,就是函数模板
学习模板不是为了写模板,而是在STL中会用系统提供的模板。
有函数模板和类模板,声明时都可以用class
template<typename T>或者template<class T>
模板函数
基本用法
语法:
//告诉编译器,要声明一个函数模板,后面的T不要报错,T是任意数据类型
template<typename T>//或者template<class T>
//紧跟的第一个函数就是模板函数
void mySwap(T &a,T &b);
调用
//第一种是指定数据类型
mySwap<int>(a, b);
//第二种是编译器自己推导
mySwap(a, b);
//a和b必须是一致的数据类型,如果a是int,b是char,就不能交换,指定数据类型也不行
mySwap<int,char>(a, c);//错误示范
如果定义了一个模板函数,但是里面没有T,调用的时候也必须指定数据类型
template<typename T>
void func()
{
cout<<"func"<<endl;
}
//调用,也得指定一个数据类型
func<int>();
注意事项:
- 自动类型推导,必须推导出一致的数据类型T,才能使用
- 模板必须确定出T的数据类型,才能使用
- 函数模板和类模板都可以用class,效果一样
比如要交换两个数,如果每个数据类型都写一个太麻烦了,写个通用的,调用的时候告诉再指定相应的数据类型就可以了,甚至都不用告诉,让编译器自己推导。
#include <iostream>
using namespace std;
//函数模板
//这样交换,数据类型不计其数,但是很多相似之处,通过模板能实现任意类型的数据交换
//交换两个整型
void swapInt(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
//交换两个浮点型
void swapDouble(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
swapInt(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
double c = 1.1;
double d = 2.2;
swapDouble(c, d);
cout << "c=" << c << endl;
cout << "d=" << d << endl;
}
//函数模板
//告诉编译器,要声明一个函数模板,后面的T不要报错,T是任意数据类型
template<typename T>
//做一个交换函数
void mySwap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
void test02()
{
int a = 10;
int b = 20;
//1.直接传入模板函数,编译器推导出T代表的数据类型
mySwap(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
//2.指定显示类型
mySwap<int>(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
局限性
内置的数据类型可以操作,但是数组,或者自定义的类,无法直接操作。这时候可以用之前的运算符重载操作,也可以对模板函数关于具体的类进行重载
#include <iostream>
using namespace std;
class Person {
public:
string m_Name;
int m_Age;
};
template<class T>
bool myCompare(T& a, T& b)
{
if (a==b)
{
return true;
}
else
{
return false;
}
}
//不能直接操作Person,可以选择对==进行运算符重载,也可以模板重载
template<>bool myCompare(Person& p1, Person& p2)
{
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
{
return true;
}
else
{
return false;
}
}
void test01()
{
int a = 1, b = 2;
bool ret = myCompare(a, b);
cout << "ret=" << ret << endl;
}
//但是对于Person数据类型不能直接操作
void test02()
{
Person p1 = { "张三",22 };
Person p2 = { "张三",22 };
bool ret=myCompare<Person>(p1, p2);
if (ret)
{
cout << "相等" << endl;
}
else
{
cout << "不相等" << endl;
}
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
类模板
基本用法
//template<class T>
//接着写的是类,就叫类模板,如果是函数,就是函数模板
注意事项:
1. 不能推导,只能指定
2. 模板参数列表可以有默认值,加上默认值之后,调用时有指定就用指定的,没有指定的就用默认的。
类外实现注意事项:
1.类模板声明
2.作用域名<T>::
3.2里面的T和上面类定义一致
#include <iostream>
#include <string>
using namespace std;
//template<class T>
接着写的是类,就叫类模板,如果是函数,就是函数模板
//但是Person有姓名和年龄,单独一个T是不够的
template<class NameType,class AgeType>
class Person {
public:
//构造函数
Person(NameType name, AgeType age);
//{
// this->m_Name = name;
// this->m_Age = age;
//}
void showInfo()
{
cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
/// <summary>
/// 类外实现,要加三个东西
/// 1.类模板声明
/// 2.作用域名<T>::
/// 3.2里面的T和上面类定义一致
/// </summary>
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void test01()
{
//分别指定数据类型
Person <string,int> p1("张三", 22);
p1.showInfo();
}
int main() {
test01();
system("pause");
return 0;
}
类模板成员函数创建时机
#include <iostream>
using namespace std;
//先创建两个类
class Person1 {
public:
//成员函数
void showPerson1()
{
cout << "Person1" << endl;
}
};
class Person2 {
public:
//成员函数
void showPerson2()
{
cout << "Person2" << endl;
}
};
//创建一个类模板
template<class T>
class myClass {
public:
//成员变量,数据类型T仍未确定
T obj;
//成员函数
void func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
void test01()
{
//这里给了数据类型
myClass<Person1>m;
//开始调用,Person1数据类型,只能调用func1,不能func2,
//但是myClass里面没有报错
m.func1();
//m.func2();
}
int main() {
test01();
system("pause");
return 0;
}
类模板对象作函数参数
利用typeid(typename).name()来打印编译器推导出来的数据类型
共有三种方式,1比较好用
- 指定传入类型,把参数类型都告诉编译器
- 参数模板化:让编译器自己推导参数模板
- 整个类模板化:把整个类作为一个T
- 利用typeid(typename).name()来打印编译器推导出来的数据类型
#include <iostream>
using namespace std;
//利用typeid(typename).name()来打印编译器推导出来的数据类型
template<class nameType, class ageType>
class Person {
public:
Person(nameType name,ageType age)
{
this->m_Name = name;
this->m_Age = age;
}
void showInfo()
{
cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}
nameType m_Name;
ageType m_Age;
};
//三种传入方式
//1.指定传入类型
void showPerson1(Person<string, int> &p)
{
p.showInfo();
}
//2.参数模板化:将对象中的参数变为模板
template<class nameType, class ageType>
void showPerson2(Person<nameType, ageType>& p)
{
p.showInfo();
//cout << "nameType的数据类型" << typeid(nameType).name() << endl;
//cout << "ageType的数据类型" << typeid(ageType).name() << endl;
}
//3.整个类模板化
template<class T>
void showPerson3(T& p)
{
p.showInfo();
cout << "T的数据类型" << typeid(T).name() << endl;
}
//调用
void test01()
{
//1.传入指定的数据类型
Person<string, int>p1("张三", 23);
showPerson1(p1);
//2.让编译器推导参数模板
Person<string, int>p2("李四", 23);
showPerson2(p2);
//3.让编译器推导整个类模板
Person<string, int>p3("王五", 23);
showPerson3(p3);
}
int main() {
test01();
system("pause");
return 0;
}
类模板继承
父类是一个类模板,子类继承时必须指定父类的数据类型。如果不想一下子把数据类型给出,则把子类也形成一个模板函数,并用模板代替父类数据类型。
#include <iostream>
using namespace std;
template<class T>
class Base {
public:
T m;
void fun()
{
cout << "T的数据类型" << typeid(T).name() << endl;
}
};
//要继承,就必须指定T的数据类型
class Son :public Base<int> {
};
//这样写更灵活,没有把int定死了,而是用的模板,调用时,子类指定父类数据类型
template<class T1,class T2>
class Son2 :public Base<T2>
{
public:
T2 obj;
void baseFun()
{
cout << "T1的数据类型:" << typeid(T1).name() << endl;
cout << "T2的数据类型:" << typeid(T2).name() << endl;
}
};
void test01()
{
Son s1;
}
void test02()
{
//这里int和char对应定义的T1,T2
//T2给了父类
Son2<int,char> s2;
s2.fun();
s2.baseFun();
}
int main() {
//test01();
test02();
return 0;
}
类模板分文件编写
由于类模板中成员函数在调用时才创建,所以分文件编写不同于之前普通的类。
有两种方式可以解决这一问题,推荐方式2
- .h和.cpp还是分开写,调用时只包含.cpp,而不包含.h
- .h和.cpp不要分开写,就写在一个文件里面,命名为.hpp,这是约定俗成的命名方式
Person.hpp,调用时直接包含该hpp文件
#pragma once
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Person {
public:
//构造函数
Person(T1 name, T2 age);
//成员函数,显示信息
void showInfo();
//成员变量
T1 m_Name;
T2 m_Age;
};
//构造函数
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
//成员函数,显示信息
template<class T1, class T2>
void Person<T1, T2>::showInfo()
{
cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}
类模板与友元
全局函数的类内实现简单,类外实现非常复杂,主要有以下几点:
- friend void outShowInfo
<>
(Person<T1, T2>& p);//<>很关键 - 类的声明,全局函数类外实现,类的实现,有顺序要求,否则编译器不认识
#include <iostream>
#include <string>
using namespace std;
//函数里面有类,要写在函数实现前面
template<class T1, class T2>
class Person;
//类外实现要写在类的前面,让编译器知道有这样的实现
//全局函数显示信息,类外实现
template<class T1, class T2>
//由于不是成员函数,不需要作用域
void outShowInfo(Person<T1, T2>& p)
{
cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
}
template<class T1, class T2>
class Person {
public:
//构造函数
Person(T1 name, T2 age);
//全局函数显示信息,类内实现
friend void inShowInfo(Person<T1,T2>& p)
{
cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
}
//全局函数显示信息,类内声明
//要是没有<>,类外实现的时候,是一个模板函数,而非全局函数,会报错
friend void outShowInfo<>(Person<T1, T2>& p);
private:
//成员变量
T1 m_Name;
T2 m_Age;
};
//构造函数
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
void test01()
{
Person<string,int> p1("张三",22);
inShowInfo(p1);
}
void test02()
{
Person<string, int> p2("李四", 22);
outShowInfo(p2);
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
类模板案例: 深拷贝、运算符重载
自定义一个数组的类模板,可以存放内置数据类型,也可以存放自定义数据类型,并且可以对数组进行增加或删除元素的操作。以下案例有三个文件,注意深拷贝与string的加法
1.自定义数组类模板:myArray.hpp
#pragma once
#include <iostream>
using namespace std;
//自己的通用的数组类
template<class T>
class myArray {
public:
//构造函数
myArray(int capacity)
{
//cout << "myArray有参构造调用" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
pAddress = new T[this->m_Capacity];
}
//拷贝构造
myArray(const myArray& arr)
{
//cout << "myArray拷贝构造调用" << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity];
//如果arr中有数据,数据也要拷贝
if (arr.pAddress!=NULL)
{
for (int i = 0; i < arr.m_Size; i++)
{
//这里不用再在堆区开辟
//pAddress[i] = new T[*arr.pAddress[i]];
//直接相等
this->pAddress[i] = arr.pAddress[i];
}
}
}
//重载=,防止浅拷贝,为了连等
myArray & operator=(const myArray& arr)
{
//cout << "=重载" << endl;
//先判断原来堆区是否有数据,有则清空
if (this->pAddress != NULL)
{
//释放数组
delete[] this->pAddress;
this->pAddress = NULL;
//容量清零
this->m_Capacity = 0;
//元素个数清零
this->m_Size = 0;
}
//开始深拷贝
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity];
//如果arr中有数据,数据也要拷贝
if (arr.pAddress != NULL)
{
for (int i = 0; i < arr.m_Size; i++)
{
//这里不用再在堆区开辟
//pAddress[i] = new T[*arr.pAddress[i]];
//直接相等
this->pAddress[i] = arr.pAddress[i];
}
}
return *this;
}
//尾插法
void Push_Back(const T &val)
{
//先判断数组是否到达容量上限
if (this->m_Size==this->m_Capacity)
{
return;
}
else
{
this->pAddress[this->m_Size] = val;
//更新数组元素个数
this->m_Size++;
}
}
//尾删法
void Pop_Back()
{
//元素个数减一,让用户访问不到
if (this->m_Size==0)
{
return;
}
this->m_Size--;
}
//通过下标访问数组元素,必须重载[]
//单是一个T可以访问元素,但是想要赋值,还得加&,如arr[0]=1;
T& operator[](int index)
{
return this->pAddress[index];
}
//析构函数
~myArray()
{
//cout << "myArray析构调用" << endl;
//释放堆区数组
if (this->pAddress!=NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
}
//获取数组容量
int getCapacity()
{
return this->m_Capacity;
}
//获取元素个数
int getSize()
{
return this->m_Size;
}
private:
//数组名,指向堆区开辟的真实数组
T* pAddress;
//容量
int m_Capacity;
//数组元素个数
int m_Size;
};
2.自定义数据类型:Person.hpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2>
class Person {
public:
Person();
//构造函数
Person(T1 name, T2 age);
//成员函数,显示信息
void showInfo();
//成员变量
T1 m_Name;
T2 m_Age;
};
//无参构造
template<class T1, class T2>
Person<T1,T2>::Person()
{
}
//构造函数
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
//成员函数,显示信息
template<class T1, class T2>
void Person<T1, T2>::showInfo()
{
cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}
3.测试案例.cpp
#include "myArray.hpp"
#include "Person.hpp"
void test01()
{
//内置数据类型的测试
myArray<int> arr1(10);
for (int i = 0; i < 10; i++)
{
arr1.Push_Back(i);
//cout << "arr1[" << i << "]=" << arr1[i] << endl;
}
arr1.Pop_Back();
myArray <int>arr2(arr1);
for (int i = 0; i < arr1.getSize(); i++)
{
cout << "arr2[" << i << "]=" << arr1[i] << endl;
}
}
void test03()
{
//自定义数据类型的测试
myArray<Person<string, int>>arr(5);
Person<string, int>p1("张三", 22);
Person<string, int>p2("张四", 23);
Person<string, int>p3("张五", 24);
Person<string, int>p4("张六", 25);
Person<string, int>p5("张七", 26);
arr.Push_Back(p1);
arr.Push_Back(p2);
arr.Push_Back(p3);
arr.Push_Back(p4);
arr.Push_Back(p5);
for (int i = 0; i < arr.getSize(); i++)
{
arr[i].showInfo();
}
}
void test04()
{
//最初的想法是这样写,但是有一个问题,arr的元素个数无法更新,必须有push_back
myArray<Person<string, int>>arr(5);
string nameSeed = "ABCDE";
for (int i = 0; i < arr.getCapacity(); i++)
{
//名字,注意不能合在一起写,
arr[i].m_Name = "Person_";
arr[i].m_Name += nameSeed[i];
arr[i].m_Age =20+rand()%10;
//不知道这样写是否合法,但是不用pushback就无法更新数组的元素个数
//也许再另外构造一个Person数组更好
arr.Push_Back(arr[i]);
//arr[i].showInfo();
}
// 没有pushback,这里的arr.getSize()=0
arr.Pop_Back();
for (int i = 0; i < arr.getSize(); i++)
{
arr[i].showInfo();
}
}
int main() {
//test01();
//test03();
test04();
system("pause");
return 0;
}
STL
STL,standard template library,标准模板库。是一套标准,分为容器、算法、迭代器三大部分,其中迭代器连接容器和算法。
迭代器实质是一个指针,指向容器中的数据的地址,解引用后得到该数据
常用容器
string
主要是赋值,查找、替换、删除、读取,截取字串等操作。
构造函数
#include <iostream>
#include <string>
using namespace std;
//string本质是一个类,构造函数原型:
//string(); //创建一个空的字符串 例如: string str;
//string(const char* s); //使用字符串s初始化
//string(const string& str); //使用一个string对象初始化另一个string对象
//string(int n, char c); //使用n个字符c初始化
void test01()
{
//1.空字符串
string s1;
cout << "s1=" << s1 << endl;
//2.使用字符串初始化
const char* str = "hello str1";
string s2(str);
cout << "s2=" << s2 << endl;
//3.初始化另一个string
string s3(s2);
cout << "s3=" << s3 << endl;
//4.使用n个字符c初始化
string s4(5, '*');
cout << "s4=" << s4 << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
赋值
- 利用=直接赋值
- 利用内置成员函数assign
#include <iostream>
#include <string>
using namespace std;
/*
赋值的函数原型:
string& operator=(const char* s); //char*类型字符串 赋值给当前的字符串
string& operator=(const string& s); //把字符串s赋给当前的字符串
string& operator=(char c); //字符赋值给当前的字符串
string& assign(const char* s); //把字符串s赋给当前的字符串
string& assign(const char* s, int n); //把字符串s的前n个字符赋给当前的字符串
string& assign(const string& s); //把字符串s赋给当前字符串
string& assign(int n, char c); //用n个字符c赋给当前字符串
*/
void test01()
{
string s1;
s1 = "hello world";
cout << s1 << endl;
string s2;
s2 = s1;
cout << s2 << endl;
string s3;
s3 = 'c';
cout << s3 << endl;
string s4;
s4.assign("hello world");
cout << s4 << endl;
string s5;
s5.assign("hello world",5);
cout << s5 << endl;
string s6;
s6.assign("abcde");
cout << s6 << endl;
string s7;
s7.assign("abcde", 3);
cout << s7 << endl;
//注意s8和s7区别,s8写法可能不合法
string s8;
s8.assign(s6, 3);
cout << s8 << endl;
string s9;
s9.assign(5, 'a');
cout << s9 << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
拼接
- 利用+=
- 利用内置成员函数append
#include <iostream>
#include <string>
using namespace std;
/*
string拼接:
string& operator+=(const char* str); //重载+=操作符
string& operator+=(const char c); //重载+=操作符
string& operator+=(const string& str); //重载+=操作符
string& append(const char* s); //把字符串s连接到当前字符串结尾
string& append(const char* s, int n); //把字符串s的前n个字符连接到当前字符串结尾
string& append(const string& s); //同operator+=(const string& str)
string& append(const string& s, int pos, int n); //字符串s中从pos开始的n个字符连接到字符串结尾
*/
void test01()
{
//简单来说,两种拼接方式,+=和append
string s1 = "welcome";
s1 += " to ";
string s2 = s1;
string s3 = s1;
s1.append("Beijing TianAnMen");
cout << s1 << endl;
//前7个
s2.append("Beijing TianAnMen", 7);
cout << s2 << endl;
//从第8开始的8个字符,0是第一个
s3.append("Beijing TianAnMen", 8,8);
cout << s3 << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
查找替换
- find(‘a’,start),可以指定从start开始
- rfind,从右往左找,但是返回的下标依旧是从左开始
- replace
#include <iostream>
using namespace std;
/*
int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找
int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找
int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
int find(const char c, int pos = 0) const; //查找字符c第一次出现位置
int rfind(const string& str, int pos = npos) const; //查找str最后一次位置,从pos开始查找
int rfind(const char* s, int pos = npos) const; //查找s最后一次出现位置,从pos开始查找
int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符最后一次位置
int rfind(const char c, int pos = 0) const; //查找字符c最后一次出现位置
string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str
string& replace(int pos, int n, const char* s); //替换从pos开始的n个字符为字符串s
*/
void test01()
{
//find
string s1 = "abcdefg hijklmncd";
int ret = s1.find("hi");
if (ret !=-1)
{
cout << "位置在:" << ret << endl;
}
else
{
cout << "未找到字符串" << endl;
}
//rfind,从右往左查,但是下标依然是左往右
int ret2 = s1.rfind("cd");
if (ret2 != -1)
{
cout << "位置在:" << ret2 << endl;
}
else
{
cout << "未找到字符串" << endl;
}
}
void test02()
{
string s1 = "abcdefg hijklmncd";
//从1号位置起的9个字符换成88
s1.replace(1, 9, "88");
cout << s1 << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
字符串比较
内置成员函数比较两串字符的ASCLL码大小,相等则返回0;
#include <iostream>
#include <string>
using namespace std;
//实质是比较两串字符的ascll
//相等返回0,大于返回1,小于返回-1
void test01()
{
string str1 = "hello world!!!";
string str2 = "hello world";
int ret=str2.compare(str1);
cout << "ret=" << ret << endl;
}
int main() {
test01();
system("pause");
return 0;
}
字符串存取
- []
- 内置成员函数.at(i)
#include <iostream>
#include <string>
using namespace std;
/*
string中单个字符存取方式有两种:
char& operator[](int n);//通过[]方式取字符
char& at(int n); //通过at方法获取字符
*/
void test01()
{
string str = "hello world!";
cout << "str=" << str << endl;
//输出单个字符
//1.[]
for (int i = 0; i < str.size(); i++)
{
//size是string的成员函数,返回字符串长度
cout << str[i] << " ";
}
cout << endl;
//2.at
for (int i = 0; i < str.size(); i++)
{
cout << str.at(i) << " ";
}
cout << endl;
//3.修改单个字符
str[0] = 'A';
str.at(1) = 'B';
cout << "str=" << str<<endl;
}
int main() {
test01();
system("pause");
return 0;
}
插入 删除 截取子串
内置成员函数insert、erase与substr
#include <iostream>
#include <string>
using namespace std;
/*
返回由pos开始的n个字符组成的字符串
string substr(int pos = 0, int n = npos) const;
*/
void test01()
{
string str = "hello world";
cout << "str=" << str << endl;
str.insert(0, "aaa");
cout << "str=" << str << endl;
str.insert(3, 5,'b');
cout << "str=" << str << endl;
//从0号位置开始删除8个字符
str.erase(0, 8);
cout << "str=" << str << endl;
}
void test02()
{
//使用操作,保留邮箱用户名
string email = "zhangsan@qq.com";
//截取一部分,另存
string sub_str = email.substr(6, 5);
cout << "sub_str=" << sub_str << endl;
int index = -1;
//for (int i = 0; i < email.size(); i++)
//{
// if (email[i]=='@')
// {
// index = i;
// }
//}
//直接用find
index = email.find('@');
string name = email.substr(0, index);
cout << "用户名:" << name << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
vector
与数组非常相似,但是数组的容量在定义时已经规定,不能扩展,但是vecotr可以动态扩展。
动态扩展:不是在原空间后面接着写,而是另找一块更大的空间,把原数据拷过来,释放原空间。
vector容器前面封闭,后面开放,数据的存入也是尾插,要想插在前面,数据得整体后移,如此一来效率低。
构造函数
#include <iostream>
#include <vector>
using namespace std;
/*
* vector 构造函数
*
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(), v.end()); //将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem); //构造函数将n个elem拷贝给本身。
vector(const vector &vec); //拷贝构造函数。
*/
//一个输出函数
void printVector(vector<int> &v)
{
for (vector<int>::iterator it=v.begin();it<v.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//无参构造
vector<int> v1;
for (int i = 0; i < 5; i++)
{
v1.push_back(i);
}
printVector(v1);
//通过区间的方式
vector<int> v2(v1.begin(), v1.end());
printVector(v2);
//n个elem赋值
vector<int> v3(5, 10);
printVector(v3);
//拷贝构造
vector<int> v4(v3);
printVector(v4);
}
int main() {
test01();
system("pause");
return 0;
}
赋值
#include <iostream>
#include <vector>
using namespace std;
/*
* 赋值操作,与构造有些类似
*
vector& operator=(const vector &vec);//重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
*/
//一个输出函数
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//先构造一个v1
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//等号赋值
vector<int> v2 = v1;
printVector(v2);
//区间赋值
vector<int> v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
//10个0赋值
vector<int> v4;
v4.assign(10, 0);
printVector(v4);
}
int main() {
test01();
system("pause");
return 0;
}
容量和大小
#include <iostream>
#include <vector>
using namespace std;
/*
empty(); //判断容器是否为空
capacity(); //容器的容量
size(); //返回容器中元素的个数
resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
*/
//一个输出函数
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//先构造一个v1
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
if (v1.empty())
{
cout << "v1为空" << endl;
}
else
{
cout << "v1不为空" << endl;
//v1里面只有10个数,也没有设置初始容量,自动扩容了
cout << "v1的容量:" << v1.capacity() << endl;
cout << "v1的大小:" << v1.size() << endl;
//尾插之后,仍继续扩容
//for (int i = 0; i < 10; i++)
//{
// v1.push_back(i);
//}
//printVector(v1);
//cout << "v1的容量:" << v1.capacity() << endl;
//cout << "v1的大小:" << v1.size() << endl;
//cout << "v1的容量:" << v1.capacity() << endl;
}
//改变容量,扩大了则补0或指定的elem;
//减少了则裁去超出的尾部
v1.resize(15);
printVector(v1);
cout << "v1的容量:" << v1.capacity() << endl;
cout << "v1的大小:" << v1.size() << endl;
v1.resize(20, 9);
printVector(v1);
cout << "v1的容量:" << v1.capacity() << endl;
cout << "v1的大小:" << v1.size() << endl;
}
int main() {
test01();
system("pause");
return 0;
}
插入 删除
#include <iostream>
#include <vector>
using namespace std;
/*
push_back(ele);//尾部插入元素ele
pop_back();//删除最后一个元素
insert(const_iterator pos, ele);//迭代器指向位置pos插入元素ele
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele
erase(const_iterator pos); //删除迭代器指向的元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
clear(); //删除容器中所有元素
*/
//一个输出函数
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//先构造一个v1
vector<int> v1;
for (int i = 0; i < 5; i++)
{
v1.push_back(i);
}
printVector(v1);
//尾删法,删除最后一个
v1.pop_back();
printVector(v1);
//插入,指定位置插入元素,(pos,ele);
v1.insert(v1.begin() + 1, 10);
printVector(v1);
//插入,指定位置插入n个元素,(pos,n,ele);
v1.insert(v1.begin() + 3, 3,20);
printVector(v1);
//删除指定位置元素
v1.erase(v1.begin() + 1);
printVector(v1);
//删除指定区间元素,这里删了3个数
v1.erase(v1.begin()+2 , v1.begin() + 5);
printVector(v1);
//clear
v1.clear();
printVector(v1);
}
int main() {
test01();
system("pause");
return 0;
}
数据读取
不使用迭代器,通过简单的[]和at函数也能访问到数据
#include <iostream>
#include <vector>
using namespace std;
//不用迭代器,使用[]和.at()读取数据
//front()和back(),读取第一个和最后一个元素
void test01()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
for (int i = 0; i < v.size(); i++)
{
//1.[]
cout << v[i] << " ";
//2. .at(i)
//cout << v.at(i)<<" ";
}
cout << endl;
//获取首个数据
cout << "第一个数据:" << v.front() << endl;;
//获取尾部数据
cout << "最后一个数据:" << v.back() << endl;;
}
int main() {
test01();
system("pause");
return 0;
}
容器互换
实际用途之一:缩小容器容量,节省空间。如一个很大的容器,里面只装了几个数据,浪费空间。通过匿名容器交换,可以缩减容量,节省空间。
#include <iostream>
#include <vector>
using namespace std;
//利用swap交换元素
//一个输出函数
void printVector(vector<int> &v)
{
for (vector<int>::iterator it=v.begin();it<v.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//无参构造
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
vector<int> v2;
for (int i = 9; i >= 0; i--)
{
v2.push_back(i);
}
printVector(v2);
cout << "交换后:" << endl;
v1.swap(v2);
printVector(v1);
printVector(v2);
}
//实用操作,缩小容器容量
void test02()
{
vector<int> v;
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
}
cout << "初始:" << endl;
cout << "v.size=" << v.size() << endl;
cout << "v.capacity=" << v.capacity() << endl;
/*
resize之后,元素个数少了,但是内存依然很大,浪费空间。
vector<int>(v) 匿名构造了一个v,大小时size 容量也是size,但是没有名字,运行完该行之后就被释放
.swap(v);匿名对象和v交换,v变小了,匿名对象被系统回收
*/
cout << "\nresize之后:" << endl;
v.resize(3);
cout << "v.size=" << v.size() << endl;
cout << "v.capacity=" << v.capacity() << endl;
cout << "\n利用swap缩小容量" << endl;
vector<int>(v).swap(v);
cout << "v.size=" << v.size() << endl;
cout << "v.capacity=" << v.capacity() << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
预留空间
预留空间:reserve(int len);控制内存开辟次数,一次性找到需要的内存,无需后续再寻找。
#include <iostream>
#include <vector>
using namespace std;
//预留空间:reserve(int len);控制内存开辟次数
void test01()
{
//无参构造
vector<int> v1;
//利用reserve预留空间,不要再找新空间,一次得到需要的大空间
//如果不加这个,底下就要找30次内存空间,有了就一步到位
v1.reserve(100000);
int count = 0;
int* p = NULL;
//这么大的数,不是一次到位的,分多次开辟新空间,
for (int i = 0; i < 100000; i++)
{
v1.push_back(i);
//p始终指向v1的首地址,变化就说明更行了
if (p!=&v1[0])
{
p = &v1[0];
count++;
}
}
cout << "开辟内存次数:" << count << endl;
}
int main() {
test01();
system("pause");
return 0;
}
deque
deque两头都可以操作,这一点上比vector效率高。但是其读取效率比vector低。
构造 赋值 大小 读取
- 构造函数:默认构造 拷贝构造 n个elem 区间
- 赋值:= assign
- 大小 empty size resize
- 数据存取:同vector,有[]、at,back和front
#include <iostream>
#include <deque>
using namespace std;
//实质是由中控器,划分成一段段缓冲区,读书效率比vector低
//但是首插效率比vector高
//deque 没有容器的概念
/*
deque<T> deqT; //默认构造形式
deque(beg, end); //构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem); //构造函数将n个elem拷贝给本身。
deque(const deque &deq); //拷贝构造函数
*/
//打印,const限制只读,迭代器也要换成只读的迭代器
void printDeque(const deque<int> &d)
{
for (deque<int>::const_iterator it=d.begin();it<d.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
//1.默认构造
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
//2.区间
deque<int> d2(d1.begin(), d1.begin()+5);
printDeque(d2);
//3.n个elem
deque<int> d3(5, 0);
printDeque(d3);
//4.拷贝构造
deque<int>d4(d1);
printDeque(d4);
}
//赋值操作
/*
deque& operator=(const deque& deq);//重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
*/
void test02()
{
deque<int>d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
deque<int>d2;
d2 = d1;
printDeque(d2);
deque<int>d3;
d3.assign(d1.begin(), d1.end());
printDeque(d3);
deque<int>d4;
d4.assign(10, 0);
printDeque(d4);
}
//大小,empty size resize 没有容量,因为可以无限扩充
void test03()
{
deque<int>d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
if (d1.empty())
{
cout << "d1为空" << endl;
}
else
{
cout << "d1的大小:" << d1.size() << endl;
}
//resize
cout << "\nafter resize" << endl;
d1.resize(3);
cout << "d1的大小:" << d1.size() << endl;
printDeque(d1);
d1.resize(20);
printDeque(d1);
d1.resize(30,5);
printDeque(d1);
}
//数据存取,同vector,有[]、at,back和front
void test04()
{
deque<int>d;
d.push_back(1);
d.push_back(2);
d.push_back(3);
cout << "d[0]=" << d[0] << endl;
cout << "d.at(1)=" << d.at(1) << endl;
cout << "d.back()=" << d.back() << endl;
cout << "d.front()=" << d.front() << endl;
}
int main() {
//1.构造函数
//test01();
//2.赋值
//test02();
//3.大小
//test03();
//4.存取
test04();
system("pause");
return 0;
}
插入 删除
- 首插法 尾插法 首删法 尾删法
- 某一位置插入 一个元素 n个元素 某一区间的数
- 清空
#include <iostream>
#include <deque>
using namespace std;
//打印,const限制只读,迭代器也要换成只读的迭代器
void printDeque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
deque<int> d1;
for (int i = 0; i < 5; i++)
{
d1.push_back(i);//尾插法
d1.push_front(i);//头插法
}
printDeque(d1);
//删除头尾
d1.pop_back();
printDeque(d1);
d1.pop_front();
printDeque(d1);
//插入,3个100,不要参数3,就是默认1个100
d1.insert(d1.begin() + 4, 3,100);
printDeque(d1);
//某一位置插入某区间的数
deque<int>d2;
d2.push_back(10);
d2.push_back(20);
d2.push_back(30);
d1.insert(d1.begin(), d2.begin(), d2.end());
printDeque(d1);
//删除某一位置
d1.erase(d1.begin() + 4);
printDeque(d1);
//删除某一区间,必须提供迭代器,不能提供给1023这样的数字索引值
d1.erase(d1.begin() + 4, d1.begin() + 6);
printDeque(d1);
//清空
d1.clear();
printDeque(d1);
}
int main() {
//1.构造函数
test01();
system("pause");
return 0;
}
排序
对于支持随即迭代器的容器,都能sort排序,需要包含头文件algorithm
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
//sort(beg,end) 排序该区间
//sort是标准算法,需要头文件algorithm
//对于支持随即迭代器的容器,都能sort排序
//输出函数,这里没有用迭代器,直接[]
void printDeque(const deque<int>& d)
{
for (int i = 0; i < d.size(); i++)
{
cout << d[i] << " ";
}
cout << endl;
}
void test01()
{
deque<int>d;
for (int i = 0; i < 10; i++)
{
d.push_back(rand() % 21);
}
cout << "排序前:" << endl;
printDeque(d);
//排序
//sort默认升序,从小到大
//对于支持随即迭代器的容器,都能sort排序
sort(d.begin(), d.end());
cout << "排序后:" << endl;
printDeque(d);
}
int main() {
test01();
system("pause");
return 0;
}
案例 评委打分
#include <iostream>
#include <vector>
#include <string>
#include <deque>
#include <algorithm>
#include <ctime>
using namespace std;
class Person {
public:
//构造函数
Person(string name, deque<int>score, int ave)
{
this->m_Name = name;
this->m_Score = score;
this->m_Ave = ave;
}
//显示个人的裁判打分
void showInfo()
{
cout << "选手姓名:" << this->m_Name << endl;
cout << "评委打分:";
for (deque<int>::iterator it=this->m_Score.begin();it!= this->m_Score.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
//算平均分
double getAve()
{
//排序,去掉最高和最低
sort(this->m_Score.begin(), this->m_Score.end());
cout << "去除最低分:" << this->m_Score.front()<<"和最高分:"<<this->m_Score.back() << endl;
this->m_Score.pop_back();
this->m_Score.pop_front();
double sum = 0;
for (deque<int>::iterator it = this->m_Score.begin(); it != this->m_Score.end(); it++)
{
sum += *it;
}
this->m_Ave = sum / this->m_Score.size();
cout << "最终成绩:" << this->m_Ave << endl << endl;
return this->m_Ave;
}
//姓名
string m_Name;
//存储裁判打分的容器
deque<int>m_Score;
//平均分
double m_Ave;
};
//初始化5名选手
vector<Person> createPerson()
{
//创建容器
vector<Person>v;
string nameSeed = "ABCDE";
for (int i = 0; i < 5; i++)
{
//姓名
string name = "Person_";
name += nameSeed[i];
//默认构造,初始化裁判打分
deque<int>d;
//平均分
int ave = 0;
//构造学生
Person p(name, d, ave);
//存入容器
v.push_back(p);
}
return v;
}
//裁判打分,传入选手容器,本来就是要赋值,不要const修饰,传入引用即可
void setScore(vector<Person> &v)
{
//10个分数,加入随机数,得放在循环外面,否则分数一样,原因未知
srand((unsigned int)time(NULL));
//开始便利
for (vector<Person>::iterator it=v.begin();it<v.end();it++)
{
具体的选手,如果初始化是10个0,先清空容器,,换成默认构造,就不需要清空
//it->m_Score.clear();
double sum = 0;
for (int i = 0; i < 10; i++)
{
//最低60 最高100
int score = rand() % 41 + 60;
//分数存入容器
it->m_Score.push_back(score);
}
}
}
//展示每个人的信息
void showScore(vector<Person>& v)
{
vector<double>v_S;
for (vector<Person>::iterator it = v.begin(); it < v.end(); it++)
{
//调用成员函数
it->showInfo();
double ave=it->getAve();
v_S.push_back(ave);
}
//排序平均分
sort(v_S.begin(), v_S.end());
//找到冠军
double max = v_S.back();
for (int i = 0; i < v.size(); i++)
{
if (v[i].m_Ave == max)
{
cout << "冠军是:" << v[i].m_Name << endl;
}
}
}
void test01()
{
//创建选手
vector<Person>v=createPerson();
//设置分数
setScore(v);
//展示分数
showScore(v);
}
int main() {
test01();
system("pause");
return 0;
}
stack和queue
stack
- stack是一种栈容器,先进后出,正因如此,stack不允许便利可以.size(),这是入栈的时候统计的,也可以判断是否为空把栈理解成一个上端开口的盒子,先进的在下面,叫栈底
- 常用的就:top pop push size empty
#include <iostream>
#include <stack>
using namespace std;
/*stack是一种栈容器,先进后出,正因如此,stack不允许便利
可以.size(),这是入栈的时候统计的,也可以判断是否为空
把栈理解成一个上端开口的盒子,先进的在下面,叫栈底,*/
//常用的就:top pop push size empty
/*
构造函数:
* stack<T > stk; //stack采用模板类实现, stack对象的默认构造形式
* stack(const stack& stk);//拷贝构造函数
赋值操作:
* stack& operator=(const stack& stk); //重载等号操作符
数据存取:
* push(elem); //向栈顶添加元素
* pop(); //从栈顶移除第一个元素
* top(); //返回栈顶元素
大小操作:
* empty(); //判断堆栈是否为空
* size(); //返回栈的大小
*
*/
void test01()
{
stack<int>s;
s.push(10);
s.push(20);
s.push(30);
s.push(40);
cout << "栈的大小:" << s.size() << endl;
cout << "栈内数据:";
//没有遍历,通过循环访问栈顶获取
while (!s.empty())
{
//返回栈顶元素
cout << s.top() << " ";
//弹出栈顶元素,下一个元素变成栈顶
s.pop();
}
cout << endl;
cout << "栈的大小:" << s.size() << endl;
}
int main() {
test01();
system("pause");
return 0;
}
queue
#include <iostream>
#include <queue>
using namespace std;
/*Queue是一种先进先出的数据结构,它有两个出口,队头和队尾可以访问,不允许遍历
依然有默认构造,拷贝构造,等号赋值,
队头出数据,队尾进入数据*/
void test01()
{
queue<int>q;
q.push(10);
q.push(20);
q.push(30);
q.push(40);
cout << "大小:" << q.size() << endl;
//没有遍历,通过循环访问栈顶获取
while (!q.empty())
{
//返回队头
cout <<"队头" << q.front() << " ";
//返回队尾
cout <<"队尾"<< q.back() << " ";
cout << endl;
//出队
q.pop();
}
cout << "栈的大小:" << q.size() << endl;
}
int main() {
test01();
system("pause");
return 0;
}
list链表
list,即链表,将数据进行链式储存,数据被存在一个个节点中,每一个节点分为数据域和指针域数据域中存放数据,指针域存放指向下一个节点地址的指针。如此,插入和删除数据更方便,但是不能随机访问,属于双向迭代器
- 如何判断叠加器是否随机 itrator it;it+1;如果不报错,就是随机的,可以+n
- 如何判断是否双向 ++ 和–都不报错就是双向
#include<iostream>
#include<list>
using namespace std;
//list,即链表,将数据进行链式储存,数据被存在一个个节点中,每一个节点分为数据域和指针域
//数据域中存放数据,指针域存放指向下一个节点地址的指针
//如此,插入和删除数据更方便,但是不能随机访问,属于双向迭代器
//如何判断叠加器是否随机 itrator it;it+1;如果不报错,就是随机的,可以+n
//如何判断是否双向 ++ 和 --不报错就是双向
void printList(const list<int> &ls)
{
for (list<int>::const_iterator it=ls.begin();it!=ls.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
//构造函数
void test01()
{
//1.默认构造
list<int>ls1;
for (int i = 0; i < 10; i++)
{
ls1.push_back(i);
}
printList(ls1);
//2.拷贝构造
list<int>ls2(ls1);
printList(ls2);
//3.区间
list<int>ls3(ls1.begin(), ls1.end());
printList(ls3);
//4.n个elem
list<int>ls4(10, 0);
printList(ls4);
}
//赋值和交换
void test02()
{
list<int>ls1;
for (int i = 0; i < 10; i++)
{
ls1.push_back(i);
}
printList(ls1);
//=
list<int>ls2 = ls1;
printList(ls1);
//assign(n,elem)
list<int>ls3;
ls3.assign(10, 0);
printList(ls3);
//assign区间
list<int>ls4;
ls4.assign(ls1.begin(), ls1.end());
printList(ls4);
//交换
ls3.swap(ls1);
printList(ls1);
printList(ls3);
}
//大小 size empty resize(n) resize(n,elem)
void test03()
{
list<int>ls1;
for (int i = 0; i < 10; i++)
{
ls1.push_back(i);
}
printList(ls1);
if (ls1.empty())
{
cout << "list为空" << endl;
}
else
{
cout << "大小:" << ls1.size() << endl;
}
ls1.resize(3);
printList(ls1);
ls1.resize(5, 0);
printList(ls1);
}
//数据存取 front和back
void test04()
{
list<int>ls1;
for (int i = 0; i < 10; i++)
{
ls1.push_back(i);
}
printList(ls1);
cout << "首元素:" << ls1.front() << endl;
cout << "尾元素:" << ls1.back() << endl;
}
/*插入和删除
首插、尾插、首删、尾删、
insert:位置元素 位置n个元素 位置区间
erase:区间 位置
clear
remove(elem) 删除所有与elem匹配的元素
*/
void test05()
{
list<int>ls1;
for (int i = 0; i < 5; i++)
{
ls1.push_back(i);
ls1.push_front(i);
}
printList(ls1);
//删除首尾巴
ls1.pop_back();
ls1.pop_front();
printList(ls1);
//insert
ls1.insert(ls1.begin(), 2, 3);
printList(ls1);
//不是连续的,不能+
//ls1.insert(ls1.begin()+2, 2, 3);
//但是可以通过迭代器
list<int>::iterator it=ls1.begin();
//it = it + 1;//防止+n,所以不让+,只能++
it++;
it++;
//双向,不能跳,只能++ --
it++;
it--;
ls1.insert(it, 2, 4);
printList(ls1);
//删除所有elem
ls1.remove(3);
printList(ls1);
//清空
ls1.clear();
printList(ls1);
}
//降序的函数
bool myCompare(int va1,int va2)
{
//降序 大就返回真 小就返回假
return va1 > va2;
}
//反转和排序
void test06()
{
list<int>ls1;
for (int i = 0; i < 10; i++)
{
//生成0到99的随机数
ls1.push_back(rand() % 10);
}
cout << "乱序:";
printList(ls1);
//升序
//所有不支持随机访问迭代器的容器,不能用标准库的sort
//这些容器会提供成员函数去排序
ls1.sort();
cout << "升序:";
printList(ls1);
//也可以自己写降序,要提供一个自定义函数
ls1.sort(myCompare);
cout << "降序:";
printList(ls1);
//反转
ls1.reverse();
cout << "反转:";
printList(ls1);
}
int main() {
//构造函数
//test01();
//赋值交换
//test02();
//大小
//test03();
//读取首位元素
//test04();
//插入删除
//test05();
//反转和排序
test06();
system("pause");
return 0;
}
排序案例
自定义排序规则
#include <iostream>
#include <list>
#include <string>
using namespace std;
//人有姓名 年龄 身高
//按年龄升序,如果年龄相同,则按身高降序
class Person {
public:
Person(string name, int age, int height)
{
this->m_Name = name;
this->m_Age = age;
this->m_Height = height;
}
string m_Name;
int m_Age;
int m_Height;
};
//创造学生
list<Person> createPerson()
{
//随机数种子
srand((unsigned int)time(NULL));
//创造5名学生
list<Person>ls;
string nameSeed = "ABCDE";
for (int i = 0; i < 5; i++)
{
string name = "Person_";
name += nameSeed[i];
//年龄18到28
int age = rand() % 11 + 18;
//身高160到190
int height = rand()%31 + 160;
//构造
Person p(name, age, height);
//存入容器
ls.push_back(p);
}
return ls;
}
//展示
void showPerson(const list<Person> &ls)
{
for (list<Person>::const_iterator it=ls.begin();it!=ls.end();it++)
{
cout << "姓名:" << it->m_Name << "\t年龄:" << it->m_Age << " 身高:" << it->m_Height << endl;
}
}
//自定义排序规则,由于Person是自定义数据类型,必须给出规则
//参数:比较两个Perosn类型
bool myCompare(Person&p1,Person&p2)
{
if (p1.m_Age==p2.m_Age)
{
//如果年龄相等,则按两人的身高降序
return p1.m_Height > p2.m_Height;
//可以继续思考,如果身高也相同,按照姓氏排列
}
else
{
//如果年龄不等,则按年龄升序
return p1.m_Age < p2.m_Age;
}
}
void test01()
{
//创建学生
list<Person>ls = createPerson();
//展示
cout << "排序前:" << endl;
showPerson(ls);
//排序,自定义数据类型,必须要有排序规则
ls.sort(myCompare);
//展示
cout << "\n排序后:" << endl;
showPerson(ls);
}
int main() {
test01();
system("pause");
return 0;
}
set
set:所有元素在插入时自动被排序,底层由二叉树实现
multiset:允许有重复元素
set:不允许容器中有重复元素
set的insert会返回一个pair数据类型,有两个值,迭代器和bool,bool可以判断是否插入成功
而multiset就没有,可以重复
#include<iostream>
#include<set>
using namespace std;
/*
set:所有元素在插入时自动被排序
底层由二叉树实现
multiset:允许有重复元素
set:不允许容器中有重复元素
*/
//打印
void printSet(const set<int>&s)
{
for (set<int>::const_iterator it=s.begin();it!=s.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
//构造 赋值
void test01()
{
/*set的insert会返回一个pair数据类型,有两个值,迭代器和bool,bool可以判断是否插入成功
而multiset就没有,可以重复*/
//默认构造
set<int>s;
//插入数据只有insert,没有push
pair<set<int>::iterator,bool>ret= s.insert(1);
if (ret.second)
{
cout << "插入成功" << endl;
}
else
{
cout << "插入失败" << endl;
}
s.insert(5);
s.insert(2);
s.insert(4);
s.insert(3);
ret=s.insert(1);//1重复了,打印的时候只有1个,可能没有传入
//第二次插入失败,因为已经有一个1了,
if (ret.second)
{
cout << "插入成功" << endl;
}
else
{
cout << "插入失败" << endl;
}
//所有元素在插入时自动排序,不允许重复值
printSet(s);
//拷贝构造
set<int>s2(s);
printSet(s2);
//赋值
set<int>s3;
s3 = s;
printSet(s3);
}
//size empty swap
void test02()
{
//默认构造
set<int>s;
//插入数据只有insert,没有push
s.insert(1);
s.insert(5);
s.insert(2);
s.insert(4);
s.insert(3);
//s.insert(5);//5重复了,打印的时候只有1个,没有传入,通过size可以知道
s.insert(6);
printSet(s);
if (s.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器大小:" << s.size() << endl;
}
set<int>s2;
s2.swap(s);
if (s.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器大小:" << s.size() << endl;
}
}
//insert clear erase:位置 区间 所有的elem
void test03()
{
//默认构造
set<int>s;
//插入数据只有insert,没有push
s.insert(1);
s.insert(5);
s.insert(2);
s.insert(4);
s.insert(3);
s.insert(7);
s.insert(6);
printSet(s);
//删除某一位置
s.erase(s.begin());
//删除所有的2,因为set不允许重复的,只有一个2
//multiset可以有多个
s.erase(2);
printSet(s);
//不允许随机访问,只能提供迭代器
//s.erase(s.begin(), s.begin() + 2);
set<int>::iterator it = s.begin();
it++;
it++;
s.erase(s.begin(), it);
printSet(s);
//清空
s.clear();
printSet(s);
}
void test04()
{
//默认构造
set<int>s;
//插入数据只有insert,没有push
s.insert(1);
s.insert(5);
s.insert(2);
s.insert(4);
printSet(s);
set<int>::iterator pos = s.find(20);
//不存在会返回s.end()
if (pos!=s.end())
{
//迭代器,实质是指针,解引用得到位置
cout << "元素位置在" << *pos << endl;
}
else
{
cout << "元素不存在" << endl;
}
//统计
int num = s.count(2);
cout << "元素个数为" << num << endl;
}
void printMultiset(const multiset<int>&ms)
{
for (multiset<int>::const_iterator it = ms.begin(); it != ms.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test05()
{
multiset<int>ms;
//可以有重复
ms.insert(30);
ms.insert(20);
ms.insert(10);
ms.insert(40);
ms.insert(10);
printMultiset(ms);
//统计
int num = ms.count(10);
cout << "元素个数为" << num << endl;
}
int main() {
//构造和赋值
test01();
//size empty swap
//test02();
//insert clear erase:位置 区间 所有的elem
//test03();
//查找和统计
//test04();
//set和multiset区别
//test05();
system("pause");
return 0;
}
pair对组两种创建方式
#include <iostream>
#include <string>
using namespace std;
//pair对组的两种创建方式
void test01()
{
//1.构造函数
pair<string, int>p1("张三",22);
cout << "p1姓名:" << p1.first << "\t年龄:" << p1.second << endl;
//2.make_pair
pair<string, int>p2=make_pair("李四", 23);
cout << "p2姓名:" << p2.first << "\t年龄:" << p2.second << endl;
}
int main() {
test01();
system("pause");
return 0;
}
set自定义排序规则-内置数据类型
#include <iostream>
#include <set>
using namespace std;
//利用仿函数修改set容器排序规则
class myCompare {
public:
//重载等号运算符,仿函数,这里必须要有const
//变成常函数,防止值被修改
bool operator()(int val1,int val2)const
{
return val1 > val2;
}
};
bool my_compare(int v1, int v2)
{
return v1 > v2;
}
void test01()
{
set<int>s1;
s1.insert(10);
s1.insert(90);
s1.insert(20);
s1.insert(50);
s1.insert(40);
s1.insert(60);
for (set<int>::iterator it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//注意调用时的迭代器与定义时对应
//这里只能用仿函数,不能用全局函数
set<int,myCompare>s2;
s2.insert(10);
s2.insert(90);
s2.insert(20);
s2.insert(50);
s2.insert(40);
s2.insert(60);
for (set<int,myCompare>::iterator it = s2.begin(); it != s2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main() {
test01();
system("pause");
return 0;
}
set自定义排序规则-自定义数据类型
#include <iostream>
#include <string>
#include <set>
using namespace std;
class Person {
public:
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
class myCompare {
public:
//必须用俩const修饰形参,防止改变实参,并且也要const变成常函数
bool operator()(const Person& p1, const Person& p2)const
{
return p1.m_Age > p2.m_Age;
}
};
void test01()
{
//自定义数据类型,必须给排序规则,否则不知道怎么插入
set<Person,myCompare>s;
Person p1("张三", 22);
Person p2("李四", 21);
Person p3("王五", 26);
Person p4("赵六", 24);
Person p5("钱七", 20);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
s.insert(p5);
for (set<Person, myCompare>::iterator it=s.begin();it!=s.end();it++)
{
cout << "姓名:" << it->m_Name << "\t年龄:" << it->m_Age << endl;
}
}
int main(){
test01();
system("pause");
return 0;
}
map
map中所有元素都是pair
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
map不允许容器中有重复key值元素,但实值可以
multimap允许容器中有重复key值元素
#include <iostream>
#include <string>
#include <map>
using namespace std;
/*
map中所有元素都是pair
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
map不允许容器中有重复key值元素,但实值可以
multimap允许容器中有重复key值元素
*/
//打印,插入时按键值排序
void printMap(const map<int, int> &m)
{
for (map<int, int>::const_iterator it=m.begin();it!=m.end();it++)
{
//解引用得到对组,对组有两个数,即键值和实值
//依然有两种方式,解引用和指针
cout << (*it).first<< " " << it->second << endl;
}
}
//构造赋值
void test01()
{
//1.默认构造
//创建map容器,由于元素是pair,有两个值
map<int, int>m;
//这里是匿名对组,详见对组的创建方式
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(4, 40));
m.insert(pair<int, int>(2, 20));
//m.insert(pair<int, int>(2, 20));//键值重复不行
printMap(m);
//2.拷贝构造
map<int, int>m2(m);
printMap(m2);
//3.等号赋值
map<int, int>m3;
m3 = m2;
printMap(m3);
}
//size empty swap
void test02()
{
map<int, int>m;
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(4, 40));
if (!m.empty())
{
cout << "The size of m is" << m.size() << endl;
}
else
{
cout << "m is empty" << endl;
}
map<int, int>m2;
m2.swap(m);
if (!m.empty())
{
cout << "The size of m is" << m.size() << endl;
}
else
{
cout << "m is empty" << endl;
}
}
//insert clear
//erase:1.pos 2.区间 3.key(索引)
//打印
void printNameMap(const map<int, string>& m)
{
for (map<int, string>::const_iterator it=m.begin();it!=m.end();it++)
{
cout << "序号:" <<it->first << "\t姓名:" << it->second << endl;
}
}
void test03()
{
//创建容器
map<int, string>m;
//insert
//1.
m.insert(pair<int, string>(1, "张三"));
//2.推荐
m.insert(make_pair(3, "李四"));
//3.
m.insert(map<int,string>::value_type(2, "王五"));
//4.如果写成了[3],会覆盖前面的
m[4]=("赵六");
//不推荐[]赋值,如果没有存m[5],系统给你创了一个空value的对组
//[]可以用来访问数据???
//cout << "m[5]=" << m[5] << endl;
//感觉都有问题,插数,key重复了,会覆盖前面,访问,不存在的key值系统会创建,所以最好别用[]
cout << "初始:" << endl;
printNameMap(m);
//删除某一位置
m.erase(m.begin());
cout << "\n删除第一个位置:" << endl;
printNameMap(m);
//也可以按照区间
//按key删除
m.erase(3);
cout << "\n删除序号3:" << endl;
printNameMap(m);
//清空
m.clear();
cout << "\n清空:" << endl;
printNameMap(m);
}
//find count
void test04()
{
map<int, int>m;
m.insert(make_pair(1, 10));
m.insert(make_pair(2, 20));
m.insert(make_pair(3, 30));
m.insert(make_pair(4, 40));
m.insert(make_pair(5, 50));
printMap(m);
map<int,int>::iterator pos=m.find(2);
if (pos != m.end())
{
cout << "找到了元素:key=" <<pos->first<<" 值:"<<pos->second << endl;
}
else
{
cout << "未找到" << endl;
}
//multimap可以大于1
int num = m.count(2);
cout << "num:" << num << endl;
}
//排序
class myCompare {
public:
bool operator()(int v1, int v2)const
{
return v1 > v2;
}
};
void test05()
{
map<int, int,myCompare>m;
m.insert(make_pair(1, 10));
m.insert(make_pair(4, 40));
m.insert(make_pair(2, 20));
m.insert(make_pair(5, 50));
m.insert(make_pair(3, 30));
//打印也必须提供对应的迭代器
for (map<int, int, myCompare>::iterator it=m.begin();it!=m.end();it++)
{
cout << (*it).first << " " << it->second << endl;
}
}
int main() {
//构造赋值
//test01();
//size empty swap
//test02();
//insert clear
//erase:1.pos 2.区间 3.key(索引)
//test03();
//find count
//test04();
//排序
test05();
system("pause");
return 0;
}
案例
- ···maltimap如何value进行排序?
- 本案例中,部门作为key进行排序,部门相同时,如何按员工工资进行排序?
公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
员工信息有: 姓名 工资组成;部门分为:策划、美术、研发
随机给10名员工分配部门和工资
通过multimap进行信息的插入 key(部门编号) value(员工)
分部门显示员工信息
#include<iostream>
#include<map>
#include<string>
#include<vector>
#include<ctime>
using namespace std;
/*
公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
员工信息有: 姓名 工资组成;部门分为:策划、美术、研发
随机给10名员工分配部门和工资
通过multimap进行信息的插入 key(部门编号) value(员工)
分部门显示员工信息
*/
class Person
{
public:
void showInfo()
{
cout << "姓名:" << this->m_Name << "\t工资:" << this->m_Salary << endl;
}
string m_Name;
int m_Salary;
};
void createWorker(vector<Person>&v)
{
//姓名种子
string nameSeed = "ABCDEFGHIJ";
//遍历
for (int i = 0; i < 10; i++)
{
Person p;
p.m_Name = "Person_";
p.m_Name += nameSeed[i];
p.m_Salary = 10000 + rand() % 10001;
v.push_back(p);
}
//for (vector<Person>::iterator it=v.begin();it!=v.end();it++)
//{
// it->showInfo();
//}
}
//员工分组
void setGroup(vector<Person>& v, multimap<int, Person>& m)
{
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
//随机部门
int dep = rand() % 3;
m.insert(pair<int, Person>(dep, *it));
}
}
void showPersonByGroup(multimap<int, Person>& m)
{
cout << "策划部:" << endl;
//pos是第一次找到0的位置,由于是已经排好位置,直接往后移动就行了
//至于移动多少,先统计一下个数
multimap<int, Person>::iterator pos = m.find(0);
int count_0 = m.count(0);//统计总人数
int index_0 = 0;
for (;index_0<count_0;pos++,index_0++)
{
pos->second.showInfo();
}
cout << "美术部:" << endl;
pos = m.find(1);
int count_1 = m.count(1);//统计总人数
int index_1 = 0;
for (;index_1 < count_1; pos++, index_1++)
{
pos->second.showInfo();
}
cout << "研发部:" << endl;
pos = m.find(2);
int count_2 = m.count(2);//统计总人数
int index_2 = 0;
for (; index_2 < count_2; pos++, index_2++)
{
pos->second.showInfo();
}
}
int main() {
srand((unsigned int)time(NULL));
//1.创建员工
vector<Person>v;
createWorker(v);
//2.设置分组
multimap<int, Person>m;
setGroup(v, m);
showPersonByGroup(m);
system("pause");
return 0;
}