C++笔记

记录下写得比较好的文章!

extern

C/C++中extern关键字详解
一、对变量和函数:
主要是对变量或函数进行声明。告知编译器,此变量在其他地方进行了定义。可能在本模块的后方、可能在其他模块中。在编译阶段找不到定义不会报错,会在链接时在其他地方找。
A.h中声明了extern变量,B.cpp include 了A.h,那么B编译时不会报错,在链接A.a时寻找。

在源文件定义了数组int a[6];
则头文件应该声明extern int a[];,而非extern int *a;

二、extern “C”:
让编译器在编译函数时,按照C语言 而非C++的方式进行编译。因C++支持重载,会在函数编译时加上参数信息。

C++调用C
1、站在被调用方角度。可以在C语言的头文件以宏的形式控制是否extern “C”:
在这里插入图片描述
这样头文件被C++ include之后,就会默认带 extern “C”。

2、站在调用方角度。如果C的头文件没有extern “C”,则调用者自己要在extern "C"内includeC的头文件。
在这里插入图片描述
归根结底,在C++眼中,C的函数声明要在extern "C"中

C调用C++:
C++头文件把函数声明放入extern "C"内。
但因C不支持extern "C"的语法,故不能直接include C++相应的头文件。而要在C文件内手动声明相应的函数或变量。
最后再链接起来。
在这里插入图片描述
三、C调用C++类:
https://blog.csdn.net/fengfengdiandia/article/details/82704375

static

c++中static的作用

右值引用

从4行代码看右值引用
作用
1、在关闭编译器的返回值优化的时候,用右值引用去承接返回值,能够减少类构造的次数;
2、在类内维护了堆内存的时候,用右值引用的移动语义,实现类的移动构造函数。在函数返回类的时候 能代替拷贝构造函数,减少临时变量对堆内存的无意义的申请释放;
3、借助移动语义、universal references、完美转发,能在模板函数内根据用户传入的值类型,调用相应的函数、或者类相应的构造函数。

universal reference
<template T> void f(T&&)
模板类型的引用,或为auto,为未定的引用类型。左值还是右值取决于传入的值。

引用折叠:
所有的右值引用叠加到右值引用上仍然还是一个右值引用
所有的其他引用类型之间的叠加都将变成左值引用

提供移动构造函数的同时,也要提供拷贝沟通函数,以防止移动失败了,还能拷贝。

完美转发:
std::forward。根据参数的实际类型进行转发(左值or右值)。

拷贝构造函数和赋值函数

C++中构造函数,拷贝构造函数和赋值函数的区别和实现

虚函数

C++ 虚函数表解析
含有虚函数的类实例化的 对象 中都有一个虚函数指针,且同一个虚函数类实例化的对象的虚函数指针都指向同一个地址,即一个类只有一个虚函数表,实例化的对象共用一个虚函数表。

如果虚函数的调用次数很多,可能会对性能有一定影响。(因为每次都要通过指针寻找函数地址)

C++11

C++11特性-概览

内存序(memory order)

理解memory order
brpc-Memory fence

SC:Sequential Consistency(顺序一致性):单核内的执行顺序和代码一致,多核之间可以相互交错
SC-DRF:Data Race Free:以加锁或atomic达到DRF。
6种order:
在这里插入图片描述
在这里插入图片描述
memory order:1、保证了重排的次序;2、保证了各个线程之间的可见性
其中,
relaxed:只确保单线程内结果与代码一致。(即在不影响单线程结果的前提下,编译器有可能调换代码顺序),但是在多线程之间的顺序不确定。

C++ memory order循序渐进(一)—— 多核编程和memory model:有时间拜读整个专栏
C++ memory order循序渐进(二)—— C++ memory order基本定义和形式化描述所需术语关系详解
C++ memory order循序渐进(三)—— 原子变量上组合应用memory order实现不同的内存序
C++ memory order循序渐进(四)—— 在std::atomic_thread_fence 上应用std::memory_order实现不同的内存序
互斥锁:悲观锁。在一个线程获得锁,其他线程都在等待的时候,这个线程被OS调度切走了,那么会导致整个程序都在等待。所以不是lock-free。
lock-free:允许单独的线程个体阻塞,但是会保证系统整体上的吞吐,如果一个算法对应的程序的线程在运行了足够长时间的情况下,至少有一个线程取得了进展,那么我们说这个算法是lock-free的。不管OS如何调度线程,至少有一个线程在做有用的事。
lock-free价值在于保证至少一个线程在做有用的事情,而不是绝对的高性能。
wait-free:工业可用的还在探索中。
order受编译时和cpu执行时的重排,而memory order提供了跨平台的限制重排

Sequentially-consistent ordering:这是约束最强的ordering,保证所有的线程观察一组内存操作的顺序完全一样。即全部线程对内存的观察,不会产生二义性。

mutex

scoped_lock:在作用域内占有至少一个互斥锁
std::scoped_lock lock(e1.m, e2.m) 等价于:

std::lock(e1.m, e2.m);
// 这里加lock_guard只是为了离开作用域时自动解锁
std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock);
std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock);

其中adopt_lock:声明互斥锁已被锁,不用再加锁
等价于:

// 这里lock_guard不加锁,lock才加锁
std::lock_guard<std::mutex> lk1(e1.m, std::defer_lock);
std::lock_guard<std::mutex> lk2(e2.m, std::defer_lock);
std::lock(lk1, lk2);

其中defer_lock:表示不进行加锁(用于延迟加锁)

try_lock
尝试lock多个互斥锁。都锁成功了则返回-1,否则返回锁失败的锁索引。

unique_lock
可移动的通用互斥锁包装器。
以及可用于条件变量。(条件变量也可以用其他包装器)

recursive_mutex
在一个线程内能被递归锁定的互斥锁。例如在一个线程,两个需要锁的函数间调用。

condition_variable

condition_variable类:
wait函数:原子地释放 lock阻塞当前线程,并将它添加到等待在 *this 上的线程列表。线程将在执行 notify_all() 或 notify_one() 时,或度过相对时限 rel_time 时被解除阻塞。
解除阻塞时,重获lock。

wait函数只支持unique_lock,是为了最高效率。
wait有两个重载版本,1:、传入lock;2、传入lock和pred函数。
如果有pred函数,需要pred返回True时,唤醒才会生效。

wait_for函数:
唤醒有两种情况:1、被notify;2、超时
有两种重载版本:1、传入lock time;2、传入lock time pred
重载版本1中,notify或超时都会唤醒。版本2中,notify且pred()=true(和wait一样) 或 超时 会唤醒。
两个版本的返回值不同,前者返回cv_status,后者返回bool。
https://zh.cppreference.com/w/cpp/thread/condition_variable/wait_for

通知线程在notify时不必保有和等待线程相同的互斥锁,(即notify之前可以手动先unlock锁),因为这样等待线程在被通知之后,立马又会再次阻塞(等锁),等待通知线程释放锁。

notify_all_at_thread_exit全局函数:
操作:1. 销毁 thread_local 对象, 2. 解锁互斥, 3. 通知 cv
(这里也是先解除互斥锁,再notify)

thread_local

SFINAE

C++模板进阶指南:SFINAE
Substitution failure is not an error

在多个模板签名完全一样的时候,会造成redefination,但其实这些模板对于不同的类型,可能会有具体不同的具体操作。这时候要在模板函数声明的时候,对参数加上限定。
例如,可以借助std::enable_if,使得T在满足一定的规则下,这个模板函数才是有效的,也即才会生效。如果不满足规则,则函数根本就不会生效。
在需要使用enable_if的时候,需要考虑是否能用其他替代:

  • 重载(对模板函数)
  • 偏特化(对模板类而言)
  • 虚函数

Expression SFINAE
用std::decay_t和decltype对形参的匹配规则加以限定。

还有一种情况只能用SFINAE:universal reference
这种情况只能用模板。
因此在这种情况下,只能用模板,再加上类型限定。

SFINAE最主要的作用:是保证编译器在泛型函数、偏特化、及一般重载函数中遴选函数原型的候选列表时不被打断。除此之外,它还有一个很重要的元编程作用就是实现部分的编译期自省和反射。

面试题

C++面试常问问题汇总

C++面试集锦( 面试被问到的问题 )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值