目录

C++

环境

编译

  • gcc主要编译C语言,对于C++代码只编译而不自动链接C++标准库,需要使用-lstdc++指定
  • g++专门编译C++代码,自动链接C++标准库
./a.out <infile> outfile # 文件重定向, infile文件中为输入

基础

关键字

extern:只声明变量,由外部定义

explicit:禁止隐式转换,即不自动将 int 转换为 string

类型限定词

  • volatile:告诉编译器变量可能会被程序外部影响并改变,不进行优化
  • mutable:只在类的成员变量中使用,mutable 成员可在 const 成员函数中修改

int型最大值最小值

INT_MAX, INT_MIN // limits.h

预定义宏

__LINE__ ; // 程序编译时当前行号
__FILE__ ; // 程序编译时当前文件名
__TIME__ ; // 被编译时间

显示访问全局变量:::a

类的成员变量:xxx_

参数:xxx

友元类

类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员

class A
{
    public:
        friend class B;
};

类型

引用:必须初始化

模板

template<typename T>
inline T const& func(T const &arg){
	return T;
}

template<typename type> ret-type func-name(parameter list); // 函数模板
template<class type> class class-name{};// 类模板 type 是占位符类型名

// 调用
cout << func<int>(1);// 不输入<int>由编译器自己判断

同样适用于类,类模版实例化需输入<type>告诉编译器类型

数组

// 动态分配数组
int* array = new int[num];   // 不初始化为0
int* array = new int[num](); // 初始化为0

delete[] array;

// 创建静态数组
int arr[num];

矩阵

二维数组,(n+1) * (m+1) 矩阵

vector<vector<bool>> f(n + 1, vector<bool>(m+1), -1);
//矩阵f, n+1 行, 每行初始化为vector<bool>(m+1) 即 m+1 列, 每个初始化为-1

结构体

定义

struct animal {
	int num;
	
	animal(int n): num(n){} //构造函数
    bool operator < (animal a) const{ // 重载函数
        return xxx;
    }
};

animal a(1);
const int N = 1000;
struct Q {
  int x;
  int y;
} que[N];

que[idx] = {t.x, t.y};

字符串

原始字符串R"(xxxx)",可以插入任何字符串而不作任何转义

  • 库类型std::string,可变长字符序列

动态分配

// 动态分配一个空字符串对象
std::string *error = new std::string("");

初始化

string s("Hello");
string s1("hello", 3); // hel
string s2("hello", 2, 2); // start, size : ll
string s3(5, 'a'); // aaaaa
string s4({'a','b','c'}); // abc
s1.assign(s2); // s1 = s2
s1.assign("h"); // s1: h
s1.assign("hello", 3); // s1: hel
s1.assign(3, 'x'); // s1:"xxx"
s1.assign({'a','b'}); // s1: "ab"

长度

int n = s.size(); // s.length()
int c = s.capacity(); // 存储空间大小

s.reserve(num); // num < capacity 不改变, > capacity 则容量改变
s.shrink_to_fit(); // 压缩存储空间大小

s.resize(20, 'x'); // 重置大小为20, 不够填充'x', 默认0

迭代器访问

for(string::iterator it = str.begin(); it != str.end(); ++it){
	*it = ' '; // 更改值
}

string n;
for(char &itr : n){
    itr--; // 字符减一
}

取值

s.front(); // 第一个字符
s.back(); // 最后一个字符

插入

s.insert(s.begin(), ' '); // 开头插入空格
s.insert(2, 3, 'x'); // 在索引2处插入3个'x'
s.insert(3, "hello"); // 在索引3处插入"hello"

s.push_back('z'); // 末尾加入元素
s.append("xx"); // 末尾加入string字符串

string str = str1 + str2; // 拼接

获取一行字符串

while(getline(cin, str)); // 字符串中可包含空格
getline(cin, s, ';'); // 停止界定符

转换为cstring

str = "abc";
str.c_str(); // "abc\0"
str.data(); // "abc"

number转换为string

s = to_string(8); // 2.3e7

string转换为number

s = "190";
int i = stoi(s); // 190

s = "190 hello";
size_t pos;
i = stoi(s, &pos); // i:190 pos=3指向非number
// stol:to long, stod: to double, stof: to float

查找

size_t found = str.find("xx"); // 找不到返回string::npos, 找到返回下标
found = s.find("u", 2, 4); // 从索引2开始找字符串前4的子串

s.find_first_of("abc");
s.find_first_not_of("abc");
s.find_last_of("abc");

删除

// 去除字符串前后空格
str.erase(0, str.find_first_not_of(" ")); // 找不到返回string::npos
str.erase(str.find_last_not_of(" ") + 1);

str.erase(start, size);
str.erase(itr, itr+5);
str.pop_back();

str.clear(); // 清空元素

交换

s1.swap(s2); // 交换2个字符串
swap(str[0], str[2]); // 交换2个字符

替换

str.replace(2, 3, "re"); // 从索引2开始的3个元素更改为"re"
str.replace(2, 3, 4, 'y'); // 从索引2开始的3个元素被替换插入4个'y'
replace(str.begin(), str.end(), 'e', ' '); // 替换字母

比较

if(str >= "10"); // 代表数字字符串可直接比较

// 相等返回0, 不匹配的第一个字符值低或字符匹配但字符串短<0, 不匹配第一个字符值大或字符串长>0
int ret = s1.compare(s2);
s1.compare(start, num, s2); // s1从start开始与s2比较num个字符

子串

sub_str = str.substr(2, 3); // 从索引2开始的3个字符拷贝

拷贝

s = "abcdefg"
char buf[20];
size_t len = s.copy(buf, 3); // buf: abc, len=3
len = s.copy(buf, 4, 2); // buf: cdef, len=4 从索引2开始拷贝4个

链表

结构体

typedef struct ListNode
{
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

void printLinkList(ListNode *head)
{
    ListNode *current = head;
    while (current != nullptr)
    {
        std::cout << current->val << " -> ";
        current = current->next;
    }
    std::cout << "nullptr" << std::endl;
}

ListNode *initLinkList(int arr[], int n)
{
    if (n == 0)
    {
        return nullptr;
    }

    ListNode *head = new ListNode(arr[0]);
    ListNode *current = head;

    for (int i = 1; i < n; ++i)
    {
        current->next = new ListNode(arr[i]);
        current = current->next;
    }

    return head;
}

class ListNode {
public:
    int val; // 节点的值
    ListNode *next; // 指向下一个节点的指针

    ListNode(int x) : val(x), next(nullptr) {} // 构造函数
    ListNode(int x){
        xxxx;
    }
};

创建

ListNode *temp = new ListNode(0), *cur = temp;	//虚拟头结点temp, cur指向temp

cur->next = new ListNode(l->value);			   	//创建一个新链表

判断指针是否为尾

while(ptr->next)

倒数第n个结点

快慢指针,使两指针移动相差n,快指针到结尾,则慢指针在倒数第n+1个结点

数据结构容器

数组

动态数组vector

定义

vector<type> name;

vector<int> pos(128, -1);// 初始化ASCII码共128个元素,每个元素值为-1

vector<pair<int,int>> tint; //每组为一对int型的数组
tint.emplace(xx, xx);
// 取数 tint[i].first, tint[i].second ...
vector<int> a({1,2,3});

比较

vector<int> xx(25, 0);
vector<int> yy(25, 0);

xx == yy; // 可直接比较

判空

vector.empty()

元素个数

vector.size()

增删

vector.push_back({xx,xx,xx})
vector.pop_back()

清空

vec.clear()

拷贝构造

vector<type> vec2(vec);

设置大小及内容

vec.resize(size, 0); // 0为填充数据

交换两个vector容器内容

vec2.swap(vec);

首元素

vector<int>::iterator it = vector.begin()// 返回迭代器

尾元素

vector.end()// 返回置迭代器,最后元素的下一个位

首尾半开放:[begin, end)

迭代器

vector<type>::iterator it;
cout << *it << endl; //取值

取数

vec[1]
vec.at(1) // 若越界会抛出异常

遍历访问

for (auto it : vec)
    cout << it << endl;
// 遍历,it不需要提前声明,vec为vector<type>

修改

int *p = &vec[0]; // 赋值p为第一个值的内存地址
p[1] = 6; // 改变vec值

静态数组array

array<int, 3> a = {1,2,3};
a.begin();
a.end();
a.size();
a.swap();

动态内存

double * pvalue = NULL;

pvalue = new double; // 堆上分配 double 类型空间,地址赋值给 pvalue
delete pvalue; // 释放单个对象

pvalue = new char[20]; // 数组分配, pvalue 指向一个数组
delete [] pvalue; // 释放数组对象

// 对象
Type_name* my_TypeArray = new Type_name[4];
delete [] my_TypeArray; // 删除数组

队列

queue

定义

queue<type> q; // 定义

初始化

queue<tuple<int, int, int>> q;
q.emplace(x, x, x); // 容器内直接构造元素
auto [x, y, time] = q.front(); // 接收元素

基础操作

q.empty() // 判空
q.size() // 元素个数
q.front() // 队列头
q.back() // 队列尾
q.pop() // 删除
q.push() // 添加

deque双端队列

deq.push_front(2); // 前插入
deq.push_back(3); // 后插入

priority_queue优先队列

priority_queue<type> pq; // 大根堆
priority_queue<type, vector<type>, greater<type>> name; // 小根堆
/* struct cmp{
		bool operator() (ListNode* a, ListNode* b){
			return a->val > b->val;
		}
	}
*/

访问

heap.push(x) // 增加
heap.empty() // 判空
heap.top() // 头元素
heap.pop() // 弹出头元素

链表

list双向链表

初始化

using listnode = std::pair<int, int>;
using listptr = typename std::list<listnode>::iterator;

std::list<listnode> linklist;

list<int> li = {1, 2, 3};

插入

list<int>::iterator itr = find(l.begin(), l.end(), 2); // itr->2
list.insert(itr, 8); // 向itr指向值前方插入8

增删

// push_front push_back
l.erase(itr); // 删除itr指向值
linklist.pop_back();
linklist.push_front({key, value});

遍历

linklist.begin();
for (auto it = mylist.begin(); it != mylist.end(); ++it){
	cout << *it << " ";
}
cout << endl;

移动

linklist.splice(linklist.begin(), linklist, listptr->second);
// arg1: 目标位置; arg2: 源链表,可为自己, arg3: 要移动的元素,迭代器指向的元素

集合

  • set:红黑树实现,自动排序,<>接收2参数:元素类型,比较器类型,每个值唯一,值不可修改
  • multiset:允许重复值,值不可修改
  • unordered_set:无序集合

定义

set<int> myset;
set<int, less<int>> s;

插入

pair<set<int>::iterator, bool> ret;
ret = myset.insert(2); // 若已有2,返回(it->2, false)
// ret.first, ret.second 迭代器可通过此获取值

查找

set<int>::iterator it;
it = myset.find(2);

删除

myset.erase(it);
myset.erase(value);

无序集合

unordered_set<int> aset;
aset.insert(xx);

// 检查
unordered_set<string>::const_iterator it = myset.find("hello");
if(it != myset.end())
    cout << *it << endl;

接口

myset.load_factor(); // 装载因子
myset.bucket(x); // x的索引
myset.bucket_count(); // 索引总数

数组转集合

unordered_set<int> us(nums.begin(), nums.end());
us.contains(xxx) // 包含

哈希表

  • 键值对存储 pair类型,map
  • multimap:允许重复值,multimap和map的键不允许修改

定义

map<type t1, type t2> mp;
unordered_map<type1, type2> hp; // 无序哈希表
hp = {
	{x1, x2}, {y1, y2}
}; // 初始化

状态

mp.empty(); // 判空
mp.size(); // 元素个数
mp.begin(); // 返回迭代器
mp.end(); // 最后一个元素的下一位
mp.reserve(100); // 预留100大小

// 取值
(*it).first;// 键
(*it).second;// 值
// *it的类型: pair<const type1, type2>

增删

map.insert(pair<type, type>(x1, x2)); // 添加,不可修改
map.insert(make_pair(x1, x2));

mp['a'] = "aa"; // 修改或插入

map.erase(); // 删除
map.erase(key);
map.clear(); // 清空

查找

mp.find(first, last, val); // [first, last)
if(mp.find(val) != mp.end()){找到val}
// 检查unordered_map中是否存在给定键的元素value
unordered_map<int, string> umap;
if(umap.count(1)){...}

遍历

for(const auto& pair : mp){
	cout << pair.first << ": " << pair.second << endl;
}

for(const auto &[x, indices] : xmap)

#include<stack>
stack<int> sk

状态

sk.empty(); //是否为空
sk.size();  //元素个数

增删

sk.push(); //添加
sk.pop();  //删除

访问

sk.top(); //栈顶元素

二叉树

struct TreeNode{
	type value;
	TreeNode *leftChild;
	TreeNode *rightChild;
};

大根堆

make_heap(vec.begin(), vec.end());

pop_heap(vec.begin(), vec.end()); // 移除最大元素
vec.pop_back(); // 移除最大元素

push_heap(vec.begin(), vec.end()); // 加入新值
vec.push_back();

// 堆排序
sort_heap(vec.begin(), vec.end());

并查集

class UnionSet{
private:
	vector<int> parent; // 父结点
    vector<int> deep; // 树深度
public:
    UnionSet(int n){
        parent.resize(n);
        deep.resize(n, 0);
        for(int i = 0; i < n; ++i){
            parent[i] = i;
        }
    }
    int find(int x){
        if(parent[x] != x){
            parent[x] = find(parent[x]); // 递归找根
        }
        return parent[x];
    }
    void union(int x, int y){
        int rootX = find(x);
        int rootY = find(y);
        if(rootX != rootY){
            if(rank[rootX] < rank[rootY]){
                rank[rootX] = rootY;
            }else if(rank[rootX] > rank[rootY]){
                rank[rootY] = rootX;
            }else{
                rank[rootX] = rootY;
                rank[rootY]++;
            }
        }
    }
}

基本操作

运算符

(a < b) ? (func1, func2) : (func3, func4)
// 逗号运算符(表达式1, 表达式2) 将会执行表达式1和表达式2,整个表达式的值为表达式2的值

取模

((x%p)+p)%p // 将任意值转移到0~p之间

数字字符char转为数字

str - '0'

字母字符转为数字

str - 'A'

初始化数组

memset(str, 0, sizeof(str));

检查字符是否为十进制数字字符

isdigit()

最值

max(a,b)
min(a,b)

查询最大(小)值所在的第一个位置

max_element(begin, end)
min_element(begin, end)
  
int ans = *max_element(dist.begin(), dist.end());
// 获取到指向某个元素的迭代器,解引用*获取该元素

整型十六进制

printf("%#x", n)

整数求对应ASCII码的符号

(char)n

字符求对应ASCII码值

int(string[i])

整型转换为二进制字符串

bitset<8> binary(int_number); // 8表示二进制串的位数
binary.to_string();

二进制字符串转换为整型

bitset<5> tempbit(string_value); // 先转换为bitset
int tempdec = (int)tempbit.to_ulong(); // 再转换为整型

输入格式

#include<bits/stdc++.h>

while(scanf("%d", &h) != EOF){
	xxx;
}

合并有序序列

// 都需要有序
merge(vec.begin(), vec.end(), // 范围1
     vec2.begin(), vec2.end(),// 范围2
     vec_out.begin());		  // 输出数组


// 原序列上合并两个有序的子序列
vector<int> vec = {1,2,3,4,1,2,3,4};
inplace_merge(vec.begin(), vec.begin()+4, vec.end()); // 1,1,2,2,3,3,4,4

类型转换

// void * 指针转换为具体类型指针
auto *xxx = static_cast<some_type *>(voidPtr);
// xxx 强制转换为 type 类型
reinterpret_cast<type>(xxx);

异常处理

throw "xxx"

try{
	xx
}catch( ExceptionName e){
	xx
}catch(const char * msg){
	cerr << msg << endl;
}cache(std::exception &e) {
  WARN("xxx", e.what());
}

计时

auto start = std::chrono::high_resolution_clock::now();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << duration.count() << std::endl;

断言

ASSERT(
        xxx,
        "'{0}', {1}",
       	arg1,
        arg2;) // arg1 替换{0}, arg2替换{1}

标准库

std::move

// 显式移动某个对象资源,避免不必要复制
std::string a = "hello";
std::string b = std::move(a); // 移动构造
// 此时 a 被“清空”,b 拥有原来 a 的资源

判断

std::isprint(ch) // 是否为可打印字符
std::isspace(ch) // 是否为空格

std::type_index

// 运行时类型识别(RTTI)
std::type_index int_idx = std::type_index(typeid(int));
int_idx.name(); // 获取类型名
// 获取表达式或类型的类型信息
typeid(type)

std::is_base_of

std::is_base_of<A, B>::value // 查看B是否继承自A,即B是A的派生类

输入输出

setw(2)
setfill('0')

gflags

用于解析输入命令行变量

DEFINE_int32(var_name, 默认值, 描述);
// 使用
gflags::ParseCommandLineFlags(&argc, &argv, true);

// 访问时
FLAGS_var_name

// 在其他文件,跨文件访问调用
DECLARE_string(var); // 类似于 extern

libconfig

libconfig::Config &cfg // 加载配置文件
cfg.readFile("xxx.cfg");// 读取文件
cfg.lookup("xxx.xxx") //

libconfig::Setting root = cfg.getRoot(); // 获取根Setting

// 修改配置
root["xxxx"] = xxx;
root[i] // 获取子元素

// 写入文件
cfg.writeFile("xxx.cfg");

root.isGroup() // 类似结构体 {}
root.isArray() // 同类型元素有序集合 []
root.isList() // 不同类型元素有序集合 ()
root.isScalar() // 标量

chrono

std::chrono::nanoseconds var; // 纳秒
std::chrono::steady_clock::now(); // 当前时间
std::chrono::steady_clock::time_point()::min(); // 时间点类型可表示的最小值:"从未发生"初始状态

高级操作

指针

std::unique_ptr:智能指针,实现独占所有权资源管理模式,动态分配内存资源只被一个指针所拥有

排序

适用于vector,deque,array等可随机访问结构

sort(vec.begin(), vec.end(), compare_function); // 通过compare_function排序
partial_sort(vec.begin(), vec.begin()+5, vec.end(), greater<int>()); // 获取最大前5个值

sort(arr.begin(), arr.end(), greater<int>()); // 升序
sort(arr.begin(), arr.end(), less<int>()); // 降序

// compare函数
bool compare_function(data a, data b) {
    if(xxx) {
        return a.val < b.val;
    }
    return a.val > b.val;
}

将满足某条件值移到最前

bool lessThanten(int i){
	return (i < 10);
}
partition(vec.begin(), vec.end(), lessThanten);
stable_partition(vec.begin(), vec.end(), lessThanten); // 保持原有顺序

逆序

reverse(str.begin(), str.end());

查找

二叉查找

bool found = binary_search(vec.begin(), vec.end(), 9); //查找9是否在

bool found = includes(vec.begin(), vec.end(), // s和vec均有序
                     s.begin(), s.end());// 若s所有元素被包含在vec中返回true

itr = lower_bound(vec.begin(), vec.end(), 9); // 找到9可以被插入保持有序的第一个位置
itr = upper_bound(vec.begin(), vec.end(), 9); // 找到9可以被插入保持有序的最后一个位置

pair_of_itr = equal_range(vec.begin(), vec.end(), 9); // 返回第一和最后的位置对

迭代器操作

  • 范围半开放:[begin, end)

计数

int n = count(vec.begin(), vec.end(), val); // 返回val个数
int n = count_if(vec.begin(), vec.end(), [](int x){return x<10;}); //返回满足条件的个数

比较

itr = max_element(vec.begin(), vec.end()); // 返回第一个最大值*itr
itr = max_element(vec.begin(), vec.end(), [](int x, int y){return (x%10)<(y%10);});
// 返回满足比较方法条件的第一个最大值

pair_of_itr = minmax_element(vec.begin(), vec.end(), [](int x, int y){return (x%10)<(y%10);}); // 返回一对迭代器

线性搜索

itr = find(vec.begin(), vec.end(), val); // 查找val,返回找到值的下一位的索引
itr = find_if(vec.begin(), vec.end(), [](int x){return x>80;});
itr = find_if_not(vec.begin(), vec.end(), [](int x){return x>80;});
itr = search_n(vec.begin(), vec.end(), count, val); // 查找连续count的val
distance(vec.begin(), itr); // itr的索引, 获取两者间距离

// 强行转换为 int 型: static_cast<int>(distance(nums.begin(), itr))

// 查找子集
itr = search(vec.begin(), vec.end(), sub.begin(), sub.end());
itr = find_end(vec.begin(), vec.end(), sub.begin(), sub.end()); // 查找最后一个子范围
// 查找到items中任何一个均可
itr = find_first_of(vec.begin(), vec.end(), items.begin(), items.end());

// 联结搜索, 默认搜索连着的相同的
itr = adjacent_find(vec.begin(), vec.end(), [](int x, int y){return x==y*4});

比较

if(equal(vec.begin(), vec.end(), vec2.begin())){} // 相同
if(is_permutation(vec.begin(), vec.end(), vec2.begin())){}   // 相同元素不同顺序
pair_of_itr = mismatch(vec.begin(), vec.end(), vec2.begin()) // 找到第一个不同
lexicographical_compare(vec.begin(), vec.end(), vec2.begin(), vec2.end()); // 一比一比较小于

检查

is_sorted(vec.begin(), vec.end())		 // 检查是否有序
is_sorted_until(vec.begin(), vec.end()); // 直到...均有序
is_partitioned(vec.begin(), vec.end(), [](int x){return x>80;}); // 检查是否以该条件区分开
is_heap(vec.begin(), vec.end())			 // 检查是否为堆
is_heap_until(vec.begin(), vec.end())    // 直到...前为堆
// 检查是否 所有/任何一个/无 元素满足条件
all_of(vec.begin(), vec.end(), [](int x){return x>80;});
any_of(vec.begin(), vec.end(), [](int x){return x>80;});
none_of(vec.begin(), vec.end(), [](int x){return x>80;});

拷贝

copy(vec.begin(), vec.end(), vec2.begin());
copy_if(vec.begin(), vec.end(), vec2.begin(), [](int x){return x>80;});

copy_n(vec.begin(), count, vec2.begin()); // 拷贝count个
copy_backward(vec.begin(), vec.end(), vec2.end());// 从后拷贝进入

移动

// 若为string可能原vec被移动的就消失了,若是int则直接类似于拷贝
move(vec.begin(), vec.end(), vec2.begin()); 
move_backward(vec.begin(), vec.end(), vec2.begin());

转移

transform(vec.begin(), vec.end(), // vec3[0] = vec[0] - 1
         vec3.begin(),
         [](int x){ return x-1;});

transform(vec.begin(), vec.end(),
          vec2.begin(),
          vec3.begin(),
          [](int x, int y) {return x+y;}); // vec3[0] = vec[0] + vec2[0]

交换

swap_ranges(vec.begin(), vec.end(), vec2.begin()); // 双向拷贝

填充

fill(vec.begin(), vec.end(), val); // 填充val
fill_n(vec.begin(), count, val);   // 填充count个val

generate(vec.begin(), vec.end(), rand); // 生成
generate_n(vec.begin(), count , rand);

替代

replace(vec.begin(), vec.end(),	// 数据范围
       6,						// 原始值
       9); 						// 新的值

replace_if(vec.begin(), vec.end(),
          [](int x){return x>80;},
          9);

replace_copy(vec.begin(), vec.end(), // 源
            vec2.begin(),		 	 // 目的
            6,						 // 原始值
            9);					     // 新值

移除

remove(vec.begin(), vec.end(), 3); // 移除所有的3
remove_if(vec.begin(), vec.end(), [](int x){return x>80;}); // 条件删除
remove_copy(vec.begin(), vec.end(), 
           vec2.begin(),
           6);// 移除所有的6,剩余值拷贝到vec2中
unique(vec.begin(), vec.end()); // 移除连续的相同的元素
unique(vec.begin(), vec.end(), less<int>()); // 移除比前一个元素大的元素
unique_copy(vec.begin(), vec.end(), vec2.begin());

倒置

reverse(vec.begin(), vec.end());
reverse_copy(vec.begin(), vec.end(), vec2.begin());

旋转

rotate(vec.begin(), vec.begin()+3, vec.end());
// 1 2 3 4 5 6 7
// 4 5 6 7 1 2 3

打乱

random_shuffle(vec.begin(), vec.end());

shuffle(vec.begin(), vec.end(), default_random_engine());

更改

// vector, deque, array等随机访问迭代器可对迭代器直接加减,比较
vector<int>::iterator itr; // 可以直接用 auto 代替
++itr;
if(itr2>itr2) ...;
itr = itr + 2;

// list, set/multiset, map/multimap等双向迭代器只可自加减
++itr;
--itr;

// forward_list等单向迭代器只可自增,不可自减
++itr;

int x = *itr;
*itr = 100; // 更改值

advance(itr, 5); // 等价于 itr+=5
std::advance(iterator, 2); // 迭代器前移2个位置
std::distance(vec.begin(), iterator); // 获取索引

流迭代器

vector<string> vec4;

// 从标准输入读取字符串到vec4
copy(istream_iterator<string>(cin), // 起始位置
     istream_iterator<string>(),    // 结束位置
     back_inserter(vec4));
// 使用 back_inserter 适配器将读取的字符串插入到 vec4 的末尾

// 将 vec4 中的字符串输出到标准输出,以空格分隔
copy(vec4.begin(), vec4.end(),
     ostream_iterator<string>(cout, " "));
// 不经过中间容器
copy(istream_iterator<string>(cin),
     istream_iterator<string>(),
     ostream_iterator<string>(cout, " ")); 

插入迭代器

vector<int> vec1 = {4, 5};
vector<int> vec2 = {12, 14, 16, 18};
vector<int>::iterator it = find(vec2.begin(), vec2.end(), 16);

insert_iterator<vector<int>> iitr(vec2, it);
copy(vec1.begin(), vec1.end(), // source
     iitr);                    // destination
                               // vec2 = {12, 14, 4, 5, 16, 18}

逆向迭代器

vector<int> vec = {4,5,6};
reverse_iterator<vector<int>::iterator> ritr;
for(ritr = vec.rbegin(); ritr != vec.rend(); ritr++){
	cout << *ritr << endl;// 6,5,4
}

常量迭代器

set<int>::const_iterator citr; // 只读不可改
for_each(myset.cbegin(), myset.cend(), MyFunction); // c++11 对每个元素执行MyFunction

函数

函数对象:重载了operator()类的实例

内置函数对象

int x = multiplies<int>()(3, 4); // x = 3 * 4
if(not_equal_to<int>()(x, 10))   // if(x!=10)

自定义函数对象

// 构造函数对象,用于将元素乘以10
class MultiplyBy10 {
public:
    int operator()(int x) const {
        return x * 10;
    }
};

std::vector<int> nums = {1, 2, 3, 4, 5};
std::vector<int> result;
// 使用 std::transform 和自定义函数对象 MultiplyBy10 将 nums 中的每个元素乘以10
std::transform(nums.begin(), nums.end(),   // 源
               std::back_inserter(result), // 目的
               MultiplyBy10());

绑定器:将函数或函数对象的某些参数绑定为固定值

transform(myset.begin(), myset.end(),		// 源
         back_inserter(vec),				// 目的
         bind(multiplies<int>(), placeholders::_1, 10));// 绑定器
		 // placeholders::_1 占位符,表示 transform 传入的每个元素
		 // 绑定了multiplies的第2个参数为10

函数转化为函数对象

double Pow(double x, double y){
    return pow(x, y);
}

auto f = function<double (double, double)>(Pow);

lambda函数

[] (int x){return (x>20)||(x<5)};

构造函数

NonCopyable() = default;  // 允许默认构造
NonCopyable(const NonCopyable&) = delete;  // 禁止拷贝构造
NonCopyable& operator=(const NonCopyable&) = delete;  // 禁止复制赋值

输入输出流

创建支持流的类

 struct Dog {
 	 int age_;
     string name_;
 };

ostream& operator<<(ostream& sm, const Dog& d){
    sm << "name is " << d.name_ << " and age is " << d.age_ << endl;
    return sm;
}

istream& operator>>(istream& sm, Dog& d){
    sm >> d.name_;
    sm >> d.age_;
    return sm;
}

int main(){
    Dog d{2, "Bob"};
    cout << d; // name is Bob and age is 2
    cin >> d;
}

格式化数据

cout.setf(ios::oct, ios::basefield); // 输出八进制
cout.setf(ios::hex, ios::basefield); // 输出十六进制
cout.setf(ios::dec, ios::basefield); // 输出十进制
cout.setf(ios::showbase); // 会输出几进制前的标识符: 0x, 0等
cout.unsetf(ios::showbase);

cout.width(10); // 宽度为10个字符
cout.setf(ios::left, ios::adjustfield); // 靠左

文件流

打开文件写

ofstream of1("h.txt"); // 创建文件写 或 打开文件并清空原内容
ofstream of2("a.txt", ofstream::app); // append加入到原内容后

ofstream of3("b.txt", ofstream::in | ofstream::out); // 从中间加入内容
of3.seekp(10, ios::beg); // 将输出指针移动到begin的后10个字符处,覆写
of3.seekp(-5, ios::end); // 离end的前5个字符处

文件位置指针

std::ifstream fileobj("data.txt", std::ios::binary);
fileobj.seekg(n); // 定位到第 n 个字节
fileobj.seekg(n, ios::cur); // 读指针从 fileobj 当前位置后移 n 个字节
fileobj.seekg(n, ios::end); // 末尾前移 n 个字节
fileobj.seekg(0, ios::end); // 定位到结尾

写入文件

of <<  "hello" << endl; // hello
of << 233 << endl; // 233
of << bitset<8>(14) << endl; // 00001110
of << complex<int>(2,3) << endl; // (2,3)
ofstream of("x.txt");
of.put('c'); // 放一个字符到流
of.write(buf, 6); // buf中6个字符输出到流
cout << ends; // '\0'
cout << flush; // 清空流
cout << setw(8) << left << setfill('_') << 99 << endl; // 99______ 宽度为8,输出不足用_填充
cout << hex << showbase << 14; // 0xe 流格式为十六进制

取整

ceil()// 向上取整
floor()// 向下取整

打开文件读

ifstream inf("h.txt");
int i;
inf >> i; // 读一个字

std::string line;
while(std::getline(inf, line)) // 读一行
    xxx

一行读取

std::ifstream file;
file.open("xx", ios::out | ios::in); // 读写
cin.getline(data, 100);
file << data << endl;
file >> data;
ifstream inf("x.txt");
char buf[80];

inf.get(buf, 80); // 读80字符到buf
inf.getline(buf, 80); // 读80直到'\n'
inf.read(buf, 20); // 读20字符
inf.ignore(3); // 忽略3字符
inf.peek(); // 返回流顶部的字符
inf.unget(); // 返回一个字符给流
inf.putback('z'); // 返回一个字符'z'给流
inf.gcount(); // 返回字符流读出的字符数

错误状态

inf.good(); // 一切正常 goodbit=1
inf.bad();  // 不可恢复错误 badbit=1
inf.fail(); // 错误流操作 failbit=1 badbit=1
inf.eof();  // 文件结束 eofbit=1

inf.clear(); // 清除所有状态
inf.clear(ios::badbit); // 为错误flag设置新值 置1
inf.rdstate(); // 读状态flag
inf.clear(inf.rdstate() & ~ios::failbit); // 只清除failbit

判断错误格式

if(inf){} // 成功读
if(inf >> i){} // 成功读

inf.exceptions(ios::badbit | iOS::failbit); // 两者中任何一个置1,抛出异常

字符串流

std::istringstream istr("12,");
int i;
char comma;

istr >> i >> comma; // i=12, comma=','

stringstream ss;

ss << 89 << "  Hex: " << hex << 89 << "  Oct: " << oct << 89;
cout << ss.str() << endl;  // 89  Hex: 59  Oct: 131

int a, b, c;
string s1;

ss >> hex >> a; // a = 137 = 0x89
ss >> s1;		// s1:"Hex:"
ss >> dec >> b; // b=59 读一个十进制的59

ss.ignore(6);
ss >> oct >> c; // c= 89 = 八进制的131

缓冲区

cout << 34;
streambuf* pbuf = cout.rdbuf();
ostream myCout(pbuf);
myCout << 34; // 34输出到标准输出

myCout.setf(ios::showpos); // 显示正负数
myCout.width(20);
myCout << 12 << endl; //                  +12
ofstream of("hello.txt");
streambuf* origBuf = cout.rdbuf();
cout.rdbuf(of.rdbuf());
cout << "hello" << endl;  // hello.txt has "hello"

cout.rdbuf(origBuf);
cout << "bye" << endl; // stdout: bye

流缓冲区迭代器

istreambuf_iterator<char> i(cin);
ostreambuf_iterator<char> o(cout);

while(*i != 'x'){
    *o = *i;
    ++o;
    ++i;
}// stdin输入啥,stdout输出啥,遇x退出

copy(istreambuf_iterator<char>(cin), istreambuf_iterator<char>(), ostreambuf_iterator<char>(cout));// 和上面基本一样,除了遇x不退出

信号处理

signal(registered signal, signal handler); // signal: SIGINT
void signalHandler(int signum) { xxx }

// raise() 生成信号
int raise(signal sig);
rasise(SIGINT);

多线程

基础值

unsigned int n = std::thread::hardware_concurrency(); // 支持的并发线程数

基础操作

std::thread t = std::thread(&Class_name::Func, this);
std::thread t1(callable_func, args...);
t1.join(); // 等待线程完成
detach(); // 将线程与主线程分离,线程后台独立运行,主线程不再等待他

// Lambda 表达式内联定义线程执行代码
std::thread t2([](int args)) {
	func_content
}, arg);

互斥量

std::mutex mtx;
mtx.lock(); // 锁定互斥锁

// 访问共享资源
mtx.unlock(); // 释放互斥锁

std::lock_guard<std::mutex> lock(mtx); // 作用域锁,构造时锁定析构时解锁,自动锁定和释放
// 访问共享资源

std::unique_lock<std::mutex> ul(mtx);
// 访问或修改共享资源
// ul.unlock(); // 可选:手动解锁

TLS

  • 线程局部存储:每个线程有自己数据副本,关键字,被其修饰的变量在每个线程中都有独立实例
thread_local int threadData = 0;

传递方法

// 值传递:参数传递
// 引用传递
func(int&xx){..}
std::thread t(func, std::ref(xx)); // 显式告诉std::thread为引用传递

条件变量

// 线程间协调,允许多个线程等待某条件发生,确保workerThread工作只能在mainThread中数据准备完毕才进行

#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void workerThread() {
    std::unique_lock<std::mutex> lk(mtx);
    cv.wait(lk, []{ return ready; }); // 等待ready变为true条件
    // 当条件满足时执行工作
}

void mainThread() {
    {
        std::lock_guard<std::mutex> lk(mtx);
        // 准备数据
        ready = true;
    } // 离开作用域时解锁
    cv.notify_one(); // 通知一个等待的线程
}

原子操作

// 多线程下,要么完全执行,要么完全不执行
std::atomic<int> count(0);
count.fetch_add(1, std::memory_order_relaxed);
// fetch-and-add 操作,给count+1, arg2: 指定该操作的内存序列模型为"放松"内存序列

线程间通信

std::promise<int> p; // 可把一个值存储到共享状态中,承诺会在某个时刻写入一个 int 类型值
std::future<int> f = p.get_future(); // 可从共享状态中读取值

std::thread t([&p] {
    p.set_value(10); // 设置值,触发 future
});

int result = f.get(); // 获取值,阻塞当前线程,直到共享状态变为就绪,直到 p 调用 set_value 为止,取出值赋值给 result

CMake

cmake_minimum_required(VERSION xxxx) # 设置最低 CMake 版本要求
add_executable(xx dir/file) # 添加源代码文件
target_link_libraries(xx xx xx) # 添加依赖库到构建系统
set(CMAKE_C_FLAGS "-xx -xx") # 设置编译选项
include_directories(xxx/xxx) # 指定头文件搜索路径

语法

继承

class class2: public class {}

派生类可以访问基类中的public和protected成员;外部类只能访问类的public成员

多态

调用成员函数时,会根据调用函数的对象的类型来执行不同的函数

虚函数:关键字为virtual,派生类重写基类中定义的虚函数时,会告诉编译器不要静态链接到该函数,而是使用动态链接根据所调用的对象类型来选择调用的函数

// 重写
void func() const override { xxxx }

包含纯虚函数的类为抽象类,不能实例化,强制派生类提供具体实现

virtual int vfunc() = 0;
Base_class *ptr; // 基类指针
ptr = new Drive_class(); // 实例化派生类
delete ptr;

重载

在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同

重载运算符

class_name operator + (const class_name &, const class_name &)
class Box
{
   public:
      // 重载 + 运算符
      Box operator+(const Box& b)
      {
         return xxx; // 调用为 B+A
      }
      void operator()(const Box& b) // 可以重载括号
      {
         cout << "x" << endl;// 调用为 B()
      }
}