收集
1,线程同步
2,函数
3,模板
4,lambda
5,方法
6,类与类方法
7,关键字
写笔记的目的是,可以方便自己很久来学的时候可以快速查阅
锁
基本互斥锁
std::mutex mtx;
互斥锁的一般用法
在函数体内锁,函数体外自动解锁
std::lock_guard<std::mutex> guard(mtx);
可以自定义锁的范围
std::unique_lock<std::mutex> lck1, lck2;
lck1 = std::unique_lock<std::mutex>(bar, std::defer_lock);
lck2 = std::unique_lock<std::mutex>(foo, std::defer_lock);
std::lock(lck1, lck2);
条件变量std::condition_variable
使用条件变量的场景
1,先获得锁(用于保证资源的共享访问)
2,wait语句释放锁,阻塞,等待其他线程notify唤醒,唤醒后,先获取锁,得到锁后,
判断条件是否返回true
如果为true,程序往下执行了,此时锁还在。
如果为false,释放锁,休眠,等待下一次notify唤醒
template<typename T>
class threadsafe_queue
{
private:
mutable std::mutex mut; // 1 互斥量必须是可变的
std::queue<T> data_queue;
std::condition_variable data_cond;
public:
void push(T new_value)
{
std::lock_guard<std::mutex> lk(mut);
data_queue.push(new_value);
data_cond.notify_one(); //生产者线程insert数据后,notify通知消费者
}
void wait_and_pop(T& value)
{
std::unique_lock<std::mutex> lk(mut);
//消费者线程收到通知,检查wait条件变量,队列不为空退出等待,取队列数据处理
data_cond.wait(lk,[this]{return !data_queue.empty();});
value=data_queue.front();
data_queue.pop();
}
};
function
//函数指针
int add1(int a, int b) {
return a + b;
}
//仿函数
struct Add {
int operator()(int a, int b) {
return a + b;
}
int a, b;
};
int main() {
auto add2 = [](int a, int b){ return a + b; }; //当然可以在()->指定后置返回类型
//auto add2 = [](int a, int b)->int { return a + b; };
function<int(int, int) > func1 = add1; //函数名
function<int(int, int) > func2 = Add(); //函数对象
function<int(int, int) > func3 = add2; //lambda表达式
std::cout << func1(3, 5) << std::endl;
std::cout << func2(3, 5) << std::endl;
std::cout << func3(3, 5) << std::endl;
while (1);
return 0;
}
std:bind
std::bind的头文件是 <functional>,它是一个函数适配器,接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。
C++11中的std::bind 简单易懂_云飞扬_Dylan的博客-CSDN博客
可调用对象
lock_guard
std::lock_guard的原理和应用_水墨长天的博客-CSDN博客
构造时,将传入的互斥量加锁,析构时,将传入的互斥量解锁,简单方便
拷贝构造函数
从零开始的移动构造函数,拷贝构造函数详解(C++)_移动拷贝构造_白铭单的博客-CSDN博客
explicit关键字
C++11 explicit关键字的详细讲解_随你而归的云彩的博客-CSDN博客
显示构造,在写代码阶段就能检测出来。也就是加上之后,写代码时会报错。
Son s1(18); //显示构造
Son s2 = 20; //隐式构造
完美转发
解决模板函数传参时,能够方便地根据传入参数(左值或者右值)实现完美传给模板函数内的函数
C/C++编译原理
预处理 -E
作用:处理宏定义和include,去除注释,不会对语法进行检查,生成.i文件
命令:gcc -E test.c -o test.i
编译 -S
作用:检查语法,生成汇编指令, .s文件。
命令:gcc -s test.c -o test.s
汇编 -C
作用: 翻译成符合一定格式的机器代码, 生成.o文件。
命令gcc -c test.c -o test.o
模板编程
模板简单示例
#include<iostream>
using namespace std;
template <typename T>
T add(T a, T b)
{
return a+b;
}
// 特化(全特化)
template <>
const char* add<const char*>(const char * str1, const char* str2)
{
std::stringstream ss;
ss << str1 <<str2;
std::string ret = ss.str();
return ret.c_str();
}
int main()
{
auto k1 = add(1231,2312);
auto k2 = add(0.23f, 123.2f);
auto k3 = add("dsf", "fdsf");
cout<<k3<<endl;
return 0;
}
/*
T可以任意写
编译器自动推导类型
汇编后的本质其实是编译器自动生成两个函数
如果类型无法推导出来,不会报错,会按函数的地址进行计算比较
*/
默认模板参数&非类型模板参数
#include<iostream>
#include<sstream>
using namespace std;
// 多个模板参数 & 非类型模板参数
// 可以用来根据输入参数的大小,实现自动分配内存
// 函数模板的本质其实是编译器推导,帮助我们写出每个不同参数的实例
// typename 可以写成class,但是一般都用typename,容易和类class定义混淆
template <typename T1, typename T2, int N>
T1* CC_ALLOC()
{
try
{
T2 a;
T1* p = new T1(N);
for(size_t i=0;i<N;i++){
p[i] = 1;
}
return p;
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return nullptr;
}
}
int main()
{
// 用封装的模板实现用new分配堆空间
auto* p = CC_ALLOC<int, int, 5>();
cout<<*p<<endl;
return 0;
}
// 如果有实例化的函数,和模板函数重复,会优先调用现用,不用去解析不存在的
int Max(int a, int b){
return a>b?a:b;
}
template<typename T>
T Max(T a, T b)
{
return a>b?a:b;
}
模板参数限定
template <typename T>
T add(T a, T b)
{
// 模板参数限定,编译的时候,如果T 不是int 也不是float,直接编译报错
static_assert(std::is_integral<T>::value || std::is_floating_point<T>::value, "Error Type CC")
return a+b;
}
非基础类型用重载操作符实现复杂业务
#include<iostream>
class Person{
private:
int _age;
public:
Person(int age): _age(age){};
// 重载>操作符
bool operator>(const Person& ref){
return this->_age > ref._age;
}
};
template<typename T>
T Max(T a, T b)
{
return a>b?a:b;
}
int main()
{
Person P1(18);
Person P2(20);
auto p3 = Max(P1, P2);
return 0;
}