文件操作
程序运行时产生的数据属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
c++中对文件操作需要包含
文件类型分为两种
- 文本文件:文件以ASCII码形式存储在计算机中
- 二进制文件:文件以文本的二进制存储在计算机中,用户一般不能直接读懂他们
操作文件三大类:
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
写文件
步骤:
-
包含头文件
#include
-
创建流对象
ofstream ofs;
-
打开文件
ofs.open(“文件路径”,打开方式)
-
写数据
ofs<<“写入的数据”;
-
关闭文件
ofs.close();
打开方式 | |
---|---|
ios::in | 读文件 |
ios::out | 写文件 |
ios::ate | 初始位置:文件尾部 |
ios::app | 追加 |
ios::trunc | 如果文件存在先删除,在创建 |
ios::binary | 二进制 |
注意:文件打开方式可以配合使用,利用 | 操作符
例如:用二进制方式写文件 ios::binary | ios::out
#include "iostream"
#include "fstream"
using namespace std;
void test() {
// 创建流对象
ofstream ofs;
ofs.open("/Users/mac/CLionProjects/c_overwrite/text.txt",ios::out);
ofs<<"姓名:张三"<<endl;
ofs<<"性别:男"<<endl;
ofs<<"年龄:18"<<endl;
// 关闭文件
ofs.close();
}
int main(int arg, char *argv[]) {
test();
return 0;
}
读文件
#include "iostream"
#include "fstream"
using namespace std;
void testWrite() {
// 创建流对象
ofstream ofs;
ofs.open("/Users/mac/CLionProjects/c_overwrite/text.txt", ios::out);
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
// 关闭文件
ofs.close();
}
void testRead() {
try {
ifstream ifs;
ifs.open("/Users/mac/CLionProjects/c_overwrite/text.txt");
if (!ifs.is_open()) {
throw -1;
}
/// 第一种
/* char buf[1024] = {0};
while (ifs >> buf) {
cout<<buf<<endl;
}*/
/// 第二种
/*char buf[1024] = {0};
while (ifs.getline(buf, sizeof(buf))) {
cout << buf << endl;
}*/
/// 第三种
/*string buf;
while (getline(ifs,buf)){
cout<<buf<<endl;
}*/
/// 第四种
char c;
while ((ifs.get()) != EOF) { /// end of file
cout << c;
}
ifs.close();
} catch (int b) {
switch (b) {
case -1:
cout << "打开失败" << endl;
}
} catch (...) {
cout << "读取失败" << endl;
}
}
int main(int arg, char *argv[]) {
testRead();
return 0;
}
读写二进制
#include "iostream"
#include "fstream"
using namespace std;
class Person {
public:
char name[64];
int age;
};
void testWrite() {
ofstream ofs("/Users/mac/CLionProjects/c_overwrite/person.txt", ios::out | ios::binary);
Person p = {"张三", 18};
ofs.write((const char *) &p, sizeof(Person));
ofs.close();
}
void testRead() {
ifstream ifs("/Users/mac/CLionProjects/c_overwrite/person.txt", ios::in | ios::binary);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
Person p;
ifs.read((char *) &p, sizeof(p));
cout << p.name << " " << p.age << endl;
}
int main(int arg, char *argv[]) {
testRead();
return 0;
}
模版
模版就是建立通用模具,大大提供复用性
函数模版
- c++ 另一种编程思想称为泛型编程,主要利用的技术就是模版
- c++提供两种模版机制:函数模版和类模版
函数模版
函数模版作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
语法
template<typename T>
函数声明或定义
解释
template — 声明创建模版
typename — 表明后面的符号是一种数据类型,也可以用class替换
#include "iostream"
using namespace std;
template<typename T>
void mySwap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int a = 1;
int b = 9;
double c = 3.3;
double d = 2.3;
/// 1. 自动类型推导
mySwap(a, b);
/// 2 显示指定类型
mySwap<double>(c, d);
cout << a << " " << b << endl;
return 0;
}
注意事项
- 自动类型推导,必须推导出已知的数据类型T才可以使用
- 模版必须要确定出T的数据类型,才可以使用
练习
#include "iostream"
using namespace std;
template<typename T>
void mySort(T arr[], int len) { /// 数组的这个长度必须传
for (int i = 0; i < len; ++i) {
T &cur = arr[i];
for (int j = i + 1; j < len; ++j) {
if (arr[j] < cur) {
T temp = cur;
cur = arr[j];
arr[j] = temp;
}
}
}
}
int main() {
int arr[] = {3, 2, 3, 4, 2, 1,};
int len = sizeof(arr) / sizeof(arr[0]);
mySort(arr, len);
for (int i = 0; i < len; ++i) {
cout << arr[i] << " ";
}
cout << endl;
char arr1[] = {'d', 'a', 'f', 'e', 'b', 'c',};
int len1 = sizeof(arr1) / sizeof(arr1[0]);
mySort(arr1, len1);
for (int i = 0; i < len1; ++i) {
cout << arr1[i] << " ";
}
return 0;
}
注意
- 普通函数调用可以发生隐式类型转换
- 函数模版 自动类型推导,不可以发生隐式类型转换
- 函数模版 显示指定类型,可以发生隐式类型转换
#include "iostream"
using namespace std;
/// 普通函数
int myAdd1(int a, int b) {
return a + b;
}
/// 函数模版
template<typename T>
T myAdd2(T a, T b) {
return a + b;
}
int main() {
int a = 10;
int b = 20;
cout << myAdd1(a, b) << endl;
char c = 'c';
cout << myAdd1(a, c) << endl;
cout << myAdd2(a, b) << endl;
/// 编译报错
// cout << myAdd2(a, c) << endl;
cout << myAdd2<int>(a, c) << endl;
return 0;
}
总结:建议使用显示类型的方式,调用函数模版,应为自己可以确定通用类型
普通函数与函数模版调用的规则
- 如果函数模版和普通函数都可以实现,优先调用普通函数
- 可以通过空模版参数列表来强制调用函数模版
- 函数模版也可以发生重载
- 如果函数模版可以产生更好的匹配优先调用函数模版
#include "iostream"
using namespace std;
void myPrint(int a, int b) {
cout << "调用普通函数" << endl;
}
template<typename T>
void myPrint(int a, int b) {
cout << "调用模版函数" << endl;
}
int main() {
int a = 20;
int b = 20;
myPrint(a, b);
myPrint<void>(a, b);
int c = 'c';
int d = 'd';
myPrint<char>(c, d);
return 0;
}
总结:一句话,调用函数模版时,传递类型参数,否则,自己都不清楚掉哪个
模版具体化
模版局限性
模版并不是万能的,有些特定数据类型,需要用具体化方式做特殊实现
#include "iostream"
using namespace std;
class Person {
string name;
public:
Person(string name) : name(name) {};
string getName() {
return name;
}
};
template<typename T>
bool myCompare(T &a, T &b) {
if (a == b) {
return true;
} else {
return false;
}
}
template<>
bool myCompare(Person &a, Person &b) {
if (a.getName() == b.getName()) {
return true;
} else {
return false;
}
}
int main() {
int a = 10;
int b = 10;
cout << myCompare(a, b) << endl;
Person p("z");
Person p1("z");
cout << myCompare(p, p1) << endl;
return 0;
}
类模版
类模版作用:
建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型代表
#include "iostream"
using namespace std;
template<class NameType, class AgeType = int>
class Person {
public:
Person(NameType name, AgeType age) : name(name), age(age) {}
public:
NameType name;
AgeType age;
void showInfo() {
cout << name << " " << age << endl;
}
};
int main() {
Person<string, int> p("张三", 3);
p.showInfo();
return 0;
}
类模版与函数模版区别
- 类模版没有自动类型推导的使用方式
- 类模版在模版参数列表中可以有默认参数
类模版中成员函数创建时机
- 普通类成员函数一开始就可以创建
- 类模版中的成员函数在调用时才创建
#include "iostream"
using namespace std;
class Person {
public:
void showPerson1() {
cout << "show person1" << endl;
}
};
template<class T>
class MyClass {
T obj;
public:
void showInfo1() {
obj.showPerson1();
}
void showInfo2() {
obj.showPerson2();
}
};
int main() {
MyClass<Person> mc;
mc.showInfo1();
/// 编译报错
// mc.showInfo2();
return 0;
}
类模版对象做函数参数
三种方式:
- 指定转入方式 – 直接显示对象的数据类型
- 参数模版化 – 将对象中的参数变为模版进行传递
- 整个类模版化 – 将这个对象类型 模版化进行传递
#include "iostream"
using namespace std;
template<class T1 = string, class T2 = int>
class Person {
public:
T1 name;
T2 age;
Person(T1 name, T2 age) : name(name), age(age) {};
void showPerson() {
cout << name << " " << age << endl;
}
};
/// 1 指定传入类型
void print1(Person<string, int> &p) {
p.showPerson();
}
/// 2 参数化模版
template<class T1, class T2>
void print2(Person<T1, T2> &p) {
cout << typeid(T1).name() << " " << endl;
p.showPerson();
}
/// 3 整个类模版化
template<class T>
void print3(T &p) {
cout << typeid(T).name() << " " << endl;
p.showPerson();
}
int main() {
Person<string, int> p1("孙悟空", 3);
print1(p1);
Person<string, int> p2("八届", 3);
print2(p1);
Person<string, int> p3("唐僧", 3);
print3<>(p3);
return 0;
}
总结:常用的是,指定传入类型
类模版与继承
- 当子类继承的父类是一个类模版是,子类在声明的时候,要指定出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中T的类型,子类也需要变为类模版
#include "iostream"
using namespace std;
template<class T>
class Base {
T m;
};
/// 此时才能计算出,子类占用多少内存空间
class Son : public Base<int> {
};
/// 如果想要灵活指定父类T类型,子类也需要变类模版
template<class T1, class T2>
class Son2 : public Base<T1> {
T2 n;
};
int main() {
return 0;
}
成员函数的类外实现
#include "iostream"
using namespace std;
template<class T1, class T2>
class Person {
public:
T1 name;
T2 age;
Person(T1 name, T2 age);
void showPerson();
};
/// 类外构造
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->name = name;
this->age = age;
}
/// 类外成员函数
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << name << " " << age << endl;
}
int main() {
return 0;
}
类模版份文件编写
问题
类模版中成员函数创建时间是在调用阶段,导致份文件编写时链接不到
解决
- 方式一:直接包含.cpp源文件
- 方式二:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
第二种方式代码
person.hpp
#include "iostream"
#include "person.h"
using namespace std;
template<class T1, class T2>
class Person {
public:
T1 name;
T2 age;
Person(T1 name, T2 age);
void showPerson();
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->name = name;
this->age = age;
}
/// 类外成员函数
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << name << " " << age << endl;
}
main.cpp
#include "iostream"
/// 第一种解决方式 直接包含源文件
//#include "person.cpp"
#include "person.hpp"
using namespace std;
int main() {
Person<string ,int> p("zhangsan",89);
p.showPerson();
return 0;
}
类模版与友元
#include "iostream"
using namespace std;
template<class T>
class Color;
template<class T>
void printInfo2(Color<T> &c) {
cout << (c.c) << endl;
}
template<class T>
class Color {
/// 全局函数类内实现
friend void printInfo1(Color<T> c) {
cout << c.c << endl;
};
/// 类外实现,比较麻烦
friend void printInfo2(Color<T> &c);
private:
T c;
public:
Color(T c) : c(c) {};
};
int main() {
Color<string> c("red");
printInfo1(c);
printInfo2(c);
// Person<string, int> p("zhangsan", 89);
// p.showPerson();
return 0;
}
总结: 建议全局函数类内实现 实现简单,类外实现,要让编译器看到
STL的诞生
- 长久以来,软件届一直希望建立一种可复用的东西
- C++的面向对象和泛型编程思想,目的就是复用性的提升
- 大多数情况下,数据结构和算法都没有一套标准,导致被迫从事大量重复工作
- 为了建立数据结构和算法的一套标准,诞生了STL
基本概念
STL(Standard Template Library,标准模版库)
STL从广义上分为:容器(container) 算法(algorithm) 迭代器(iterator)
容器和算法之间通过迭代器进行无缝连接
STL几乎所有的代码都采用了模版类和模版函数
6大组件
STL大体分为6大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,存放数据
- 算法:各种常用算法
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或者迭代器接口的东西
- 空间配置器:负责空间的配置和管理
容器、算法、迭代器
-
容器
数据、链表、树、队列、集合、映射表等
分类:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
-
算法:问题之解法也
分类:
质变算法:运算过程期间会更改元素的内容。例如拷贝、替换、删除等等
非质变算法:运算过程中不会更改期间的元素。例如查找、计算、遍历、寻找极值
-
迭代器:容器和算法之间粘合剂
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段可以理解为指针
种类 功能 支持运算 输入迭代器 只读访问 只读, ++ == != 输出迭代器 只写访问 只写,++ 前向迭代器 读写操作,并能向前推进 读写,++ == != 双向迭代器 读写操作,并能向前和向后操作 读写,++ – 随机迭代器 读写操作,功能最强迭代器 读写,++ – [n] -n < <= > >= 容器初识
#include "iostream" #include "vector" using namespace std; void myPrint(int value) { cout << value << endl; } void test1() { vector<int> v; v.push_back(10); v.push_back(20); v.push_back(30); /// 第一种 /* vector<int>::iterator itBegin = v.begin();/// 起始迭代器 vector<int>::iterator itEnd = v.end();/// 结束迭代器 while (itBegin != itEnd) { cout << *itBegin << endl; itBegin++; }*/ /// 第二种 /*for (vector<int>::iterator itBegin = v.begin(); itBegin != v.end(); itBegin++) { cout << *itBegin << endl; }*/ /// 第三种 for_each(v.begin(), v.end(), myPrint); } int main() { test1(); return 0; }
string
本质:
string 是c++风格的字符串,而string本质上是一个类
string 和 char* 区别:
char* 是一个指针
string是一个类,类内封装了char*,管理整个字符串,是一个char*型的容器
构造
string();
string(const char* s);
string(const string& str);
拷贝构造
string(int n ,char c);
赋值
#include "iostream"
#include "vector"
using namespace std;
int main() {
/// 赋值操作
string str = "hello world";
str = 'c';
string str1 = str;
str.assign("assign");
str.assign(str);
str.assign("hfsfs",3);
cout << str << endl;
return 0;
}
追加
#include "iostream"
#include "vector"
using namespace std;
int main() {
/// 追加操作
string str = "hello world";
string s1 = "world";
str+=s1;
str+='c';
/// 类似提供 append 唯一的不同
str.append("hellowlro ",3,5);
cout<<str<<endl;
return 0;
}
查找和替换
#include "iostream"
#include "vector"
using namespace std;
int main() {
/// 追加操作
string str = "hello world d";
/// 查找 从左查 第一次出现的位置,还有一个位置的参数
int dIndex = str.find("d");
cout<<dIndex<<endl;
/// 查找 从右查 第一次出现的位置,还有一个位置的参数
int rdIndex = str.rfind("d");
cout<<rdIndex<<endl;
/// 替换
str.replace(0,1,"aaaa");
cout<<str<<endl;
return 0;
}
string 字符串比较
比较方式
-
字符串比较是按字符的ASCI码进行对比
= 返回0
> 返回 1
< 返回 -1
#include "iostream"
#include "vector"
using namespace std;
int main() {
/// 字符串比较是否相等
string a = "中国";
string b = "中国";
cout<<a.compare(b)<<endl;
return 0;
}
string获取修改字符
int main() {
/// 获取单个字符,中文不适用的
string a = "中国力量,中国标准,中国制造";
cout << a[0] << endl;
cout << a.at(0) << endl;
return 0;
}
string插入
#include "iostream"
#include "vector"
using namespace std;
int main() {
string str = "hello world";
str.insert(0,"aaa");
str.insert(0,"bbb",2);/// 后边这个参数,感觉着实意义不大(指定插入几个字符)
cout<<str<<endl;
str.erase(0,3);
cout<<str<<endl;
return 0;
}
获取子串
#include "iostream"
#include "vector"
using namespace std;
int main() {
/// 一个中文 三个字符
string str = "中国力量";
string s1 = str.substr(0,3);
cout<<s1<<endl;
return 0;
}
vector
功能:
vector数据结构和数组非常相似,也称为单端数组
vector与普通数组区别
不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:
并不是在原空间后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
vector容器的迭代器是支持随机访问的迭代器
构造函数
vector v
vector(v.begin(),v.end())
vector(n,element)
vector(const vector &vec)
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
}
int main() {
/// 1
vector<int> v1;
for (int i = 0; i < 10; ++i) {
v1.push_back(i);
}
/// 2
vector<int> v2(v1.begin(),v1.end());
/// 3
vector<int> v3(10,100);
/// 4
vector<int> v4(v3);
printVector(v3);
return 0;
}
赋值
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
}
int main() {
vector<int> v1;
for (int i = 0; i < 10; ++i) {
v1.push_back(i);
}
vector<int> v2(10,100);
/// 运算符重载
v1 = v2;
/// 指定迭代器
v1.assign(v2.begin(),v2.end());
/// n个elem
v1.assign(10,101);
printVector(v1);
return 0;
}
容量和大小
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
}
int main() {
vector<int> v1;
for (int i = 0; i < 33; ++i) {
v1.push_back(i);
}
/// 是否为空
cout<<v1.empty()<<endl;
/// 容量: 每次扩容为上一次的2倍
cout<<v1.capacity()<<endl;
/// 大小
cout<<v1.size()<<endl;
/// 重新指定大小
// v1.resize(15);
printVector(v1);
return 0;
}
插入与删除
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
}
int main() {
vector<int> v1;
for (int i = 0; i < 10; ++i) {
/// 尾部插入
v1.push_back(i);
}
/// 尾部删除
v1.pop_back();
/// 指定pos插入
vector<int>::iterator begin = v1.begin();
v1.insert(begin, 100);
/// 插入两个
v1.insert(begin, 2, 200);
/// 删除
v1.erase(begin);
/// 删除迭代器从start到end
// v1.erase(begin,v1.end());
v1.clear();
printVector(v1);
return 0;
}
数据存取
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
}
int main() {
vector<int> v1;
for (int i = 0; i < 10; ++i) {
v1.push_back(i);
}
/// 1 获取数据
int i3 = v1.at(3);
int i4 = v1[4];
cout<<i4<<endl;
/// 2 获取收尾元素
int front = v1.front();
int back = v1.back();
printVector(v1);
return 0;
}
互换容器
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
/*vector<int> v1;
for (int i = 0; i < 10; ++i) {
v1.push_back(i);
}
vector<int> v2;
for (int i = 10; i < 20; ++i) {
v2.push_back(i);
}
v1.swap(v2);
printVector(v1);
printVector(v2);*/
/// 利用swap巧妙收缩
vector<int> v;
for (int i = 0; i < 10000; ++i) {
v.push_back(i);
}
cout << "容量 " << v.capacity() << endl;
cout << "大小 " << v.size() << endl;
v.resize(3);
vector<int>(v).swap(v);// 互换以size为准
cout << "容量 " << v.capacity() << endl;
cout << "大小 " << v.size() << endl;
return 0;
}
预留空间
#include "iostream"
#include "vector"
using namespace std;
void printVector(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
/// 利用swap巧妙收缩
vector<int> v;
/// 利用reserve 预留空间
v.reserve(100000);
int num = 0;// 统计开辟次数
int *p = NULL;
for (int i = 0; i < 100000; ++i) {
v.push_back(i);
if (p != &v[0]) {
p = &v[0];
num++;
}
}
cout << num << endl;
return 0;
}
deque
双端数组
deque与vector区别
- vector对于头部的插入删除效率低,数据量大,效率低
- deque相对而言,对头部的插入数据比vector块
- vector访问元素是的速度会比deque块,这和两者内部实现有关
deque内部工作原理
deque内部有个中空器,维护每段缓冲区的内容,缓冲区中存放真实数据
中空器维护的是每个缓冲区的地址,是的使用deque时像一片连续的内存地址
deque容器的迭代器也是支持随机访问的
构造函数
deque deqT
deque<beg,end> deqT
deque<n,ele> deqT
deque<const deque &deq> deqT
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
void printDeque(const deque<int> &d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
deque<int> deq;
for (int i = 0; i < 10; ++i){
deq.push_back(i);
}
deque<int > d1(deq.begin(),deq.end());
deque<int > d2(10,100);
deque<int > d3(d2);
printDeque(d3);
return 0;
}
赋值操作
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
void printDeque(const deque<int> &d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
deque<int> deq;
for (int i = 0; i < 10; ++i){
deq.push_back(i);
}
deq = deq;
deq.assign(deq.begin(),deq.end());
deq.assign(10,100);
printDeque(deq);
return 0;
}
大小操作
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
void printDeque(const deque<int> &d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
deque<int> deq;
for (int i = 0; i < 10; ++i){
deq.push_back(i);
}
cout<<deq.size()<<endl;
cout<<deq.empty()<<endl;
deq.resize(3);
deq.resize(22,2);
printDeque(deq);
return 0;
}
插入和删除
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
void printDeque(const deque<int> &d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
deque<int> deq;
for (int i = 0; i < 10; ++i){
deq.push_back(i);
}
///两端
deq.push_back(3);
deq.push_front(3);
deq.pop_back();
deq.pop_back();
// 指定位置
deq.insert(deq.begin(),32);
deq.insert(deq.begin(),2,32);
// deq.insert(3,deq.begin(),deq.end());
// deq.clear();
deq.erase(deq.begin(),deq.end());
deque<int>::iterator it = deq.begin();
*it = 10;
deq.erase(it);
printDeque(deq);
return 0;
}
存取
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
void printDeque(const deque<int> &d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
deque<int> deq;
for (int i = 0; i < 10; ++i){
deq.push_back(i);
}
deq.at(3);
deq[3] = 3;
deq.front();
deq.back();
printDeque(deq);
return 0;
}
排序
#include "iostream"
#include "vector"
#include "deque"
using namespace std;
void printDeque(const deque<int> &d) {
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
deque<int> deq;
for (int i = 0; i < 10; ++i) {
deq.push_back(i);
}
deq.at(3);
deq[3] = 10;
sort(deq.begin(), deq.end());
printDeque(deq);
return 0;
}
stack
先进后出
#include "iostream"
#include "vector"
#include "deque"
#include "stack"
using namespace std;
int main() {
stack<int> stk;
stack<int> stk1(stk);
stk1 = stk;
stk.push(3);
stk.push(4);
stk.pop();
stk.top();
cout<<stk.empty()<<endl;
cout<<stk.size()<<endl;
return 0;
}
queue
#include "iostream"
#include "vector"
#include "deque"
#include "stack"
#include "queue"
using namespace std;
int main() {
queue<int> q;
queue<int> q1(q);
q1 = q;
q.push(3);
q.push(4);
q.pop();
q.back();
q.front();
cout << q.empty() << endl;
cout << q.size() << endl;
return 0;
}
list
功能:链式存储
链表:是一种物理存储单元上非连续的存储结构,数据元素顺序是通过链表中的指针链接实现的
链表的组成:链表是由一系列节点组成
节点的组成:数据域 和 指针域
STL中的链表是一个双向循环链表
构造
list l
list<beg,end> l
list<n,ele> l
list<const list &lis> l
赋值和交换
#include "iostream"
#include "list"
using namespace std;
void printList(const list<int> &l){
for (list<int>::const_iterator it= l.begin(); it != l.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
}
int main() {
list<int> l;
l.push_back(3);
l.push_back(3);
l.push_back(3);
list<int> l1 = l;
cout<<l1.size()<<endl;
l1.push_back(4);
printList(l);
printList(l1);
l.swap(l1);
printList(l);
printList(l1);
return 0;
}
大小操作
size()
empty()
resize(num)
resize(num,elem)
插入删除
remove(elem)
删除指定的值
数据存取
front()
back()
反转和排序
reverse()
sort()
#include "iostream"
#include "list"
using namespace std;
class Person {
string name;
int age;
int height;
public:
Person(string name, int age, int height) : name(name), age(age), height(height) {}
public:
void showInfo() const {
cout << name << " " << age << " " << height << endl;
}
int getAge(){
return age;
}
int getHeight(){
return height;
}
};
void printList(const list<Person> &pList) {
for (list<Person>::const_iterator it = pList.begin(); it != pList.end(); it++) {
it->showInfo();
}
}
bool comparePerson(Person &p1,Person &p2){
if(p1.getAge() == p2.getAge()){
return p1.getHeight()<p2.getHeight();
}
return p1.getAge() < p2.getAge() ;
}
int main() {
Person p1("张三", 20, 130);
Person p2("李四", 20, 120);
Person p3("王五", 24, 130);
Person p4("赵六", 32, 90);
Person p5("天气", 10, 120);
Person p6("王八", 20, 180);
list<Person> pList = {
p1, p2, p3, p4, p5, p6
};
pList.sort(comparePerson);
printList(pList);
return 0;
}
set/multiset
自动排序
本质:关联式容器,底层二叉树
set/multiset 区别:
set 不允许有重复元素
multiset 允许存在重复元素
#include "iostream"
#include "set"
using namespace std;
void printSet(const set<int> &sets) {
for (set<int>::const_iterator it = sets.begin(); it != sets.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
set<int> s1;
s1.insert(1);
s1.insert(2);
s1.insert(3);
s1.insert(4);
printSet(s1);
/// 大小
set<int> s2;
cout << s1.size() << endl;
cout << s1.empty() << endl;
s2.swap(s1);
/// 插入删除
s1.clear();
// s1.erase(3);// 位置
s1.erase(2);// 元素
/// 查找和统计
s1.find(2);
s1.count(2);
return 0;
}
对组
#include "iostream"
#include "set"
using namespace std;
void printSet(const set<int> &sets) {
for (set<int>::const_iterator it = sets.begin(); it != sets.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
pair<string,int> p("tom",23);
cout<<p.first<<" "<<p.second<<endl;
pair<string,int> p1 = make_pair("jeery",23);
cout<<p1.first<<" "<<p1.second<<endl;
return 0;
}
排序
重写内置类型排序规则
#include "iostream"
#include "set"
using namespace std;
class MyCompare{
public:
bool operator()(int v1,int v2){
return v1 > v2;
}
};
int main() {
set<int,MyCompare> s1;
s1.insert(20);
s1.insert(30);
s1.insert(40);
s1.insert(20);
s1.insert(10);
for ( set<int,MyCompare>::iterator it = s1.begin(); it != s1.end(); it++){
cout<<*it<<" ";
}
return 0;
}
自定义类型
#include "iostream"
#include "set"
using namespace std;
class Person {
string name;
int age;
int height;
public:
Person(string name, int age, int height) : name(name), age(age), height(height) {}
public:
void showInfo() const {
cout << name << " " << age << " " << height << endl;
}
int getAge() const {
return age;
}
int getHeight() const {
return height;
}
};
class MyCompare {
public:
bool operator()(const Person &v1, const Person &v2) {
return v1.getAge() > v2.getAge();
}
};
int main() {
Person p1("张三", 20, 130);
Person p2("李四", 20, 120);
Person p3("王五", 24, 130);
Person p4("赵六", 32, 90);
Person p5("天气", 10, 120);
Person p6("王八", 20, 180);
set<Person, MyCompare> s;
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
s.insert(p5);
s.insert(p6);
for (set<Person, MyCompare>::iterator it = s.begin(); it != s.end(); it++) {
it->showInfo();
}
return 0;
}
map/multimap
简介
- map中所有的元素都是pair
- pair中第一个元素为key(键值),起到索引作用,第二个为value(实值)
- 所有的元素会根据元素的键值自动排序
本质:关联式容器,二叉树
优点:快速查找value
map和multimap区别:key是否重复
#include "iostream"
#include "map"
#include "fstream"
using namespace std;
void printMap(map<int, string> &m) {
for (map<int, string>::iterator it = m.begin(); it != m.end(); it++) {
cout << it->first << " " << it->second << endl;
}
}
int main() {
map<int, string> m;
m.insert(pair<int, string>(1, "10"));
m.insert(pair<int, string>(2, "20"));
m.insert(pair<int, string>(3, "30"));
map<int, string> m1(m);
map<int, string> m2 = m;
printMap(m);
m.size();
m.empty();
m.swap(m1);
m.clear();
m.erase(m.begin());
m.erase(m.begin(),m.end());
cout<<m[2]<<endl;
m.find(2);
m.count(2);
return 0;
}
这些api不要硬记,记住底层的结构即可,方法大同小异
容器总结
序列容器
List封装链表,Vector封装了数组
Vector:向量
相当于一个数组,在内存中分配一块连续的内存空间进行存储。支持不指定vector大小的存储。STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacity()函数返回的大小,当超过此分配的空间时在整体重新分配一块内存地址,这个人以vector可以不指定vector即一个连续内存的大小的感觉。通常此默认的内存分配能完成大部分情况下的存储
优点:
1. 连续内存,向数组一样操作
2. 随机访问方便,支持[]和at
3. 节省空间
缺点:
1. 插入删除效率低
2. 只能在vector的最后进行push和pop,不能在vector的头进行push和pop
3. size > capacity 时,重新分配、拷贝与释放
List:双向链表
每个节点都包括一个信息块、一个前驱指针Pre、一个后驱指针Post。可以不分配必须的内存大小,方便的进行添加和删除操作。使用的是非连续的内存空间进行存储
优点:
1. 不使用连续内存完成动态操作
2. 在内部方便的进行插入和删除操作
3. 可在两端进行push、pop
缺点:
1. 不能进行内部的随机访问,即不支持[]和at
2. 相对vector占用内存多
deque:双端队列
deque是在功能上合并了vector和list
优点:
1. 随机访问方便,即支持[]和at
2. 在内部方便的进行插入和删除操作
3. 可在两端进行push、pop
使用区别
1. 如果需要高效随机存取,而不在乎插入和删除的效率,使用vector
2. 如果需要大量的插入和删除,而不关心随机存取,则应使用list
3. 如果需要随机存取,而且关心两端数据的插入和删除,则应使用deque
关联容器
Map、Set属于标准关联容器,使用了非常高效的平衡检索二叉树:红黑树,他的插入删除效率比其他容器高是因为不需要做内存拷贝和内存移动,而直接替换指向节点的指针即可
Set和Vector在于Set不包含重复数据。Set和Map的区别在于Set只含有Key,而Map有一个Key和Key对应的Value两个元素
Map和Hash_Map的区别是Hash_Map使用了Hash算法来加快查找过程,但是需要更多的内存来存放这些hash桶元素,因此可以算得上空间换时间的策略