八股临时总结

文章目录

一、原子操作的内部屏障

1、原子操作的功能

(1)保证这个变量的读写不可被打断;

(2)保证操作前后的内存访问顺序不会被编译器或CPU重排序;

2、原子内部屏障

(1)CAS(无锁同步机制,通过比较内存中的值是否等于期望值,如果相等则更新为新值,否则失败并重试)

(2)memory-barrier(内存屏障 )防止CPU和编译器为了优化,把代码执行顺序乱掉

release/acquire通常通过memory barrier实现

acquire和release

Acquire Release
保证当前线程读到的数据是最新的 保证本线程对数据的修改是其他线程可见的
cpp 复制代码
#include <atomic>
#include <iostream>
#include <thread>

std::atomic<bool> ready{false};
int data = 0;

void producer() {
    data = 42;          // 步骤 1: 准备数据
    // Release: 保证 data = 42 绝对不会被重排到这句代码的【后面】
    ready.store(true, std::memory_order_release); 
}

void consumer() {
    // Acquire: 保证读到 true 之后,后续的代码绝对不会被重排到这句代码的【前面】
    while (!ready.load(std::memory_order_acquire)) {
        // 等待
    }
    
    // 步骤 3: 读取数据
    // 绝对安全!因为 Acquire 和 Release 配对,
    // producer 中在 release 之前的所有修改(data=42),
    // 在 consumer 中 acquire 之后必然可见!
    std::cout << data << std::endl; // 永远输出 42
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

二、虚函数表实现原理

2.1 为什么需要虚函数?

C++默认函数调用都是静态绑定:

cpp 复制代码
class Base {
public:
    void func(){
        cout<<"Base";
    }
};

class Derived: public Base {
public:
    void func(){
        cout<<"Derived";
    }
};


Base* ptr = new Derived();
ptr->func();

输出:Base

因为在编译阶段就已经确定调用了Base::func

如果希望:利用基类Base类型指针调用派生类func,则需要虚函数!

cpp 复制代码
class Base{
public:
	virtual void func(){
		cout<<"Base";
	}
};

这就是多态!

2.2 虚函数表是什么?

当一个类包含虚函数时,编译器会给这个类增加一个隐藏成员:虚函数表;

调用过程:

cpp 复制代码
Base* p = new Derived();

p->func();
  1. 根据p找到对象
  2. 找到对象里的vptr
  3. 根据vptr找到vtable
  4. 找到对应函数地址
  5. 调用派生类Derived::func()

通过虚函数表记录,实现动态绑定;

2.3 虚函数表的开销?

2个:

(1)对象增加一个指针:vptr(8字节)

(2)多一次间接寻址:加载 vptr -> 加载 func address -> 调用

2.4 析构函数为什么经常写virtual?

普通的析构函数,在通过基类ptr执行析构函数时,不会将其派生类对象析构掉,会造成:内存泄露。

回答总结:

  • C++虚函数通过vptr和vtable实现运行时多态。包含虚函数的对象内部维护一个虚函数表指针,指向存储虚函数地址的表。
  • 当通过基类指针调用虚函数时,会根据vptr查找到其真实函数的地址,从而调用派生类实现;

三、智能指针使用场景和内存管理

普通指针会存在忘记delete的问题,所以利用智能指针方便管理(直接自动管理)

类型 特点 场景
unique_ptr 唯一所有权 资源管理 :谁创建谁释放
shared_ptr 共享所有权 多个模块共享,存在问题:多线程同步
weak_ptr 观察,不拥有 解决循环引用

四、多线程同步机制

多个线程同时访问共享资源导致数据竞争

重量级方式:

4.1 mutex互斥锁

cpp 复制代码
std::mutex m;


m.lock();

count++;

m.unlock();

4.2 atomic原子操作

针对简单变量:

cpp 复制代码
std::atomic<int> count;

count++;

内部支持CAS

读取旧值

比较是否改变

如果没有改变

更新

否则重试

4.3 生产者、消费者模式

消费者:没有数据就wait()

生产者:开始生产就notify()

4.4 semaphore信号量

控制资源数量

例如:GPU任务队列

acquire()获取

执行任务

release()释放

相关推荐
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
weilx12344 天前
C++笔记-文件IO-<fcntl.h>
c++
Smileyqp沛沛4 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光4 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·4 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁4 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven4 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
程序猿编码4 天前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎
此生决int4 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++