RVO(Return Value Optimization) 返回值优化NRVO(Name Return Value Optimization) 命名返回值优化禁用 RVO首先看一下禁用掉 RVO 的效果#include <iostream> using namespace std; class Test { public: int a; Test() { cout << "Test::Test()" << endl; } Test(const Test&) { cout << "Test(const Test&)" << endl; } Test(Test&&) noexcept{ cout << "Test(Test&&)" << endl; } ~Test() {
call 指令的语法call addresscall 指令的操作过程保存当前指令指针(EIP/PC): call 指令会将当前 EIP 的值(指向下一条指令的地址)压入栈中。这样做的目的是保存调用位置,以便在子程序或函数执行完成后能够返回到调用位置。跳转到目标地址: 接下来, call 指令会将控制权传送到目标地址处,执行目标地址所指向的代码。跳转到被调函数中后栈结构如下high --------------------------------- 主调函数的局部变量.. --------------------------------- .. --------------------------------- 传递给被调函数的参数.. --------------------------------- .. --------------------------------- 返回地址 : 返回主调函数要执行的指令地址, ret 指令之前先 调整 rsp 指向这里,ret 指令读取返回 ---------------------------------
使用汇编可以直接控制 CPU 指令, 使用 c++ 可以调用到汇编代码, 这里记录一下调用汇编的流程以一个汇编实现的swap 函数为例首先在 c++ 层我们需要有一个函数声明extern "C" { extern 返回类型 函数名(参数列表) asm("汇编符号名"); };示例extern "C" void asm_swap(void *a, void *b) asm("asm_swap");这里要注意几点extern "C" 要防止 c++ 编译器进行名称修饰,C++ 的名称修饰会导致链接失败函数名与 asm() 内 汇编符号一致asm() 内代表汇编符号名, 外部汇编函数必须与声明保持一致添加一个汇编文件示例 : swap.S.global asm_swap # 声明 swap 为全局符号,使得该符号可以被其他文件或模块引用。 asm_swap: # 表明后续的指令是该函数的实现。 movq (%rdi), %rax # 实现 swap 逻辑, movq 是移动 8 个字节 movq (%rs
编写 CMakeLists.txt 文件 名称固定注释 #使用 ${} 使用变量的值定义和赋值变量 set(var val)clion 生成的cmake 文件cmake_minimum_required(VERSION 3.27) # 需要的cmake最低版本 project(Proj) # 项目名称 set(CMAKE_CXX_STANDARD 17) # set 函数, 设置变量的值, CMAKE_CXX_STANDARD 是使用的 c++ 标准 add_executable(Proj main.cpp) # 使用指定的源文件来生成目标可执行文件, 第一个参数为 target 常用命令set 设置变量的值add_library 类似 add_executable , 只不过他来生成库文件示例 add_library(proj add.cpp) aux_source_directory在 add_executable 或者 add_library 一个个文件添加确实可以,但是不太优雅,使用这个命令可以自动识别源文件保存到一个变量中示例 aux_source_directory(
当我们使用 std::thread 创建线程时, 他会忽略线程函数的返回值, 假如我们要获取这个线程的返回值,就要通过全局变量或者传递引用的再加上一些同步机制的方式获取返回值。这样做确实可以实现,但是很麻烦,不如直接使用 c++ 帮我们封装好的模板类future, promise以及async 这几个模板类就是帮我们做这件事的std::future头文件 future简介 顾名思义,future 就像一个存储着未来结果的容器,它代表了一个异步操作的结果。我们可以检查结果是否已经有了, 或者阻塞等待这个结果std::promise头文件 简介 直接看代码#include <chrono> #include <thread> #include <iostream> #include <future> using namespace std; void f(std::promise<int>& pro) { this_thread::sleep_for(chrono::seconds(1)); pro.s
std::ref 用于取某个变量的引用比如当我们使用std::thread 创建一个线程时#include <thread> #include <iostream> using namespace std; void f(int a) { a++; cout << a << endl; } int main() { int a = 0; thread t1(f, a); t1.join(); cout << a << endl; }输出1 0这个线程函数是以值传递参数时时没有问题的, 线程函数内修改形参不会影响实参,但是假如线程函数是引用传递就需要使用std::ref了看这样的代码#include <thread> #include <iostream> using namespace std; void f(int& a) { a++; cout << a << endl; }
先来看这样一个模板函数template <typename T> void f(T&& t) {}对于这样的模板参数 ,推导规则如下当传入的表达式为左值时 T 会被推导为 T&例如int a = 0; f(a);这里的 T 就会被推导为 int&当传入的表达式为右值时 T 会被推导为 T例如f(0);这里的 T 就会被推导为 int对于第一种情况, T&& t 会被推导为 T& && t, 这就成了左值引用的右值引用了, 在 C++ 中 其实是不可以出现引用的引用的, 这里就提出了一个引用的概念:如果定义出一个引用的引用,则这些引用会形成 折叠, 也就是引用折叠X& &, X& &&, X&& & 会被折叠为 X&X&& && 会被折叠为 X&&只有右值引用的右值引用才会折叠成右值引用,其他情况都是左值引用对于 T&& 参数,如果传入左值就会折叠成 int&,传入右值就会折叠成 int&&所以 对于 T&& 类型的模板参数类型,也叫做万能引用
c++ 17 后 标准库提供了std::shared_mutex, 其实就是读写锁。这里记一下读写锁的特性没有线程持有写锁时,所有线程都可以一起持有读锁有线程持有写锁时,所有的读锁和写锁都会阻塞有线程持有读锁时,写锁都会阻塞,读锁可以直接持有因为不同线程同时读数据是没有数据一致性的问题,只有在有线程写数据时才会有数一致性问题, 而互斥锁不管是读还是写,都会独占资源的所有权,当我们读多写少的时候,就可以使用读写锁来提升性能头文件#include <shared_mutex>成员函数读锁lock() 加写锁, 会阻塞直到加锁成功try_lock() 尝试加写锁,加锁成功或失败都会返回unlock() 解除写锁写锁lock_shared() 加读锁,会阻塞直到加锁成功try_lock_shared() 尝试加读锁,加锁成功或失败都会返回unlock_shared() 解除读锁与 std::lock_guard 、std::unique_lock 或 std::shared_lock 配合使用。1. 配合 std::lock_guard 、std::unique_lock, 在
C++11 中提供的条件变量, 需要配合 mutex 和 unique_lock 使用头文件#include <condition_variable>使用方法等待函数首先声明一个 unique_lock<mutex> lock(mtx);condition_variable 有以下几个 wait 函数wait() : 用于阻塞线程并等待唤醒。有两个重载其中一个只需要传递一个 unique_lock另一个 需要传递一个unique_lock 和一个 lambda 表达式, 这个lambda 用来判断某些条件,在收到其它线程的通知后仅仅有当 它返回值为 true 时才会被解除堵塞wait_for() : 在指定的超时时间内它,阻塞线程并等待唤醒, 两个重载传递 unique_lock 和 一个 chrono::duration 类型的对象,代表一段时间, 功能与wait() 的第一个重载一样, 只多了一个超时时间传递 unique_lock 、chrono::duration 和 一个判断条件 的 lambda 表达式, 功能类似wait() 第二个重载返回值如果条
头文件 #include <mutex>lock_guard守卫锁 通过 RAII 机制: 创造对象的时候加锁,离开作用域的时候解锁;保证了在它的作用域内都是加锁的,也省去了我们手动加锁解锁的麻烦需要配合 mutex 使用示例#include <mutex> using namespace std; mutex m; int mian() { { // 在这个花括号的范围内, 是持有锁的 lock_guard<mutex> lock(m); // do something... } return 0; }unique_locklock_guard 虽然lock_guard省去了我们手动加锁解锁的麻烦,但是不够灵活,unique_lock就解决了这个问题unique_lock 不仅有和lock_guard 一样的功能, 还可以手动的加锁和解锁示例#include <mutex> using namespace std; mutex m; int mian() { { // 在这个花括号的范围
lelele