读书笔记:《Effective Modern C++(C++14)》
Effective Modern C++(C++14)
GitHub - CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C++》- 完成翻译
Deducing Types
- 模版类型推导:
- 引用,const,volatile被忽略
- 数组名和函数名退化为指针
- 通用引用:
- 左值保持引用和const
- 右值忽略引用和const
- auto 类型推导:
- {} 初始化被auto推导为std::initializer_list
- 模版推导无法解析 {} 初始化
- 不可见的代理类可能会使auto从表达式中推导出“错误的”类型
- decltype 类型推导:
- 将decltype应用于变量名会产生该变量名的声明类型
- 返回decltype-》auto + decltype尾返回类型
- auto 与 decltype(auto)不同,decltype保留了auto忽略的左值引用
decltype(x+y) add(T x, U y){}; // 编译错误template <typename T, typename U>
auto add(T x, U y) -> decltype(x+y)
{ return x+y;
} template <typename T, typename U> // C++ 14
auto add(T x, U y)
{ return x+y;
}decltype(auto) f1()
{int x = 0;return x; //decltype(x)是int,所以f1返回int
}decltype(auto) f2()
{int x = 0;return (x); //decltype((x))是int&,所以f2返回int&
}
- 查看类型推导结果:
- 编译期:编译期报错诊断
- 运行时:
- typeid:依赖编译期解析,可能不正确
- Boost.TypeIndex
template<typename T> //只对TD进行声明
class TD; //TD == "Type Displayer"
// 如果尝试实例化这个类模板就会引出一个错误消息,因为这里没有用来实例化的类模板定义。为了查看x和y的类型,只需要使用它们的类型去实例化TD:
TD<decltype(x)> xType; //引出包含x和y
TD<decltype(y)> yType; //的类型的错误消息template<typename T>
void f(const T& param)
{using std::cout;cout << "T = " << typeid(T).name() << '\n'; //显示Tcout << "param = " << typeid(param).name() << '\n'; //显示
} //的类型#include <boost/type_index.hpp>template<typename T>
void f(const T& param)
{using std::cout;using boost::typeindex::type_id_with_cvr;//显示Tcout << "T = "<< type_id_with_cvr<T>().pretty_name()<< '\n'; //显示param类型cout << "param = "<< type_id_with_cvr<decltype(param)>().pretty_name()<< '\n';
}
Modern C++:98 vs 11
- 构造,拷贝构造,拷贝复制
Widget w1; //调用默认构造函数
Widget w2 = w1; //不是赋值运算,调用拷贝构造函数
w1 = w2; //是赋值运算,调用拷贝赋值运算符(copy operator=)
- 初始化:(),=,{}
- ():构造,无参构造和函数声明语义冲突,无参构造应该省略()
- =:拷贝构造,拷贝赋值
- {}:统一初始化,不允许类型变窄
- std::initializer_list 和 {} 初始化永远是最佳匹配
- () 初始化和 {} 初始化可能会有巨大不同:ex:std::vector
- 重载指针参数和整型参数:
- 0:int
- NULL:int or long
- nullptr:std::nullptr_t,可以隐式转换为指向任何内置类型的指针
- **typedef(C++98)和using(C++11):**using别名模版更简单,不需要::type和typename
template<typename T> //MyAllocList<T>是
using MyAllocList = std::list<T, MyAlloc<T>>; //std::list<T, MyAlloc<T>>
MyAllocList<Widget> lw; //用户代码template<typename T> //MyAllocList<T>是
struct MyAllocList { //std::list<T, MyAlloc<T>>typedef std::list<T, MyAlloc<T>> type; //的同义词
};
MyAllocList<Widget>::type lw; //用户代码template<typename T>
class Widget { //Widget<T>含有一个
private: //MyAllocLIst<T>对象typename MyAllocList<T>::type list; //作为数据成员
}; template <class T>
using remove_const_t = typename remove_const<T>::type;
template <class T>
using remove_reference_t = typename remove_reference<T>::type;
template <class T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;
- 非限域Enum(C+98)和限域Enum(C++11):限域class enum不污染命名空间,都支持底层类型,但是Enum class不能隐式转换只能cast
enum Color { black, white, red }; //black, white, red在Color所在的作用域
auto white = false; //错误! white早已在这个作用域中声明enum class Color { black, white, red }; //black, white, red限制在Color域内
auto white = false; //没问题,域内没有其他“white”
Color c = white; //错误,域中没有枚举名叫white
Color c = Color::white; //没问题using UserInfo = //类型别名,参见Item9std::tuple<std::string, //名字std::string, //email地址std::size_t> ; //声望enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; //同之前一样
auto val = std::get<uiEmail>(uInfo); //啊,获取用户email字段的值template<typename E> //C++14
constexpr std::underlying_type_t<E>toUType(E enumerator) noexcept
{return static_cast<std::underlying_type_t<E>>(enumerator);
}
auto val = std::get<toUType(UserInfoFields::uiEmail)>(uInfo);
- 未定义私有声明(C++98)和deleted(C++11):
- 删除自动生成的函数:都可
- 删除隐式转换类型的函数重载:only deleted
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
private:basic_ios(const basic_ios& ); // not definedbasic_ios& operator=(const basic_ios&); // not defined
};template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:basic_ios(const basic_ios& ) = delete;basic_ios& operator=(const basic_ios&) = delete;
};bool isLucky(int number); //原始版本
bool isLucky(char) = delete; //拒绝char
bool isLucky(bool) = delete; //拒绝bool
bool isLucky(double) = delete; //拒绝float和doubletemplate<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
class Widget {
public:template<typename T>void processPointer(T* ptr)
};
template<> //还是public,
void Widget::processPointer<void>(void*) = delete; //但是已经被删除了
- 显式override和final来实现重载和避免重载:C++11新增引用限定符
class Widget {
public:using DataType = std::vector<double>;DataType& data() & //对于左值Widgets,{ return values; } //返回左值DataType data() && //对于右值Widgets,{ return std::move(values); } //返回右值
private:DataType values;
};
- 优先使用const_iterator而非iterator
- **begin,end,cbegin,rbegin的非成员函数通用性更强,**可用于原生数组
- **尽可能使用noexcept:**可以编译优化,noexcept函数可以调用异常中立函数
RetType function(params) noexcept; //极尽所能优化 //C++11风格,没有来自f的异常
RetType function(params) throw(); //较少优化 //C++98风格,没有来自f的异常
RetType function(params); //较少优化
- **const 常量,编译期不一定可知;constexpr 编译期常量,编译期可知;constexpr 函数值根据参数在编译期是否可知决定,**constexpr函数既可以是运行时也可以是编译期
- const成员函数中的mutable成员变量,需要mutex或atomic来保证线程安全
- 特殊成员函数:构造,析构,拷贝构造,拷贝赋值,移动构造,移动赋值
- **Role of Three:**自定义资源管理时,析构,拷贝构造,拷贝赋值同时自定义
- 移动操作仅当类没有显式声明移动操作,拷贝操作,析构函数时才自动生成
- 在自定义移动时,相应拷贝被deleted,在自定义析构时,拷贝不自动生成
- **置入emplace_back优于插入push_back:**避免了临时对象的创建和销毁
Smart Pointers
- **unique_ptr:专有所有权,大小与原始指针相同,不占用额外内存资源,**完全拥有指向内容的所有权,只能移动不能复制,可以转化为shared_ptr
- **shared_ptr:共享所有权,大小是原始指针二倍,包含引用计数,**引用计数在堆上,内存动态分配,原子操作增减
- 使用maked_shared创建,或直接传递new指针,不要传递原始指针变量
- 如果原始指针变量存在,且存在多个基于原始指针的shared_ptr,会存在多次析构的问题
- **weak_ptr:从shared_ptr创建,不拥有所有权,**只能判断原指针是否过期,不能解引用
- 获取shared_ptr:lock(),shared_ptr()
- 避免shared_ptr循环引用无法释放
- 作为缓存,观察者,只判断是否过期
- make_shared, make_unique:
- 异常安全,异常时不会导致内存泄漏
- 对象内存和引用计数内存一次分配
- 控制块和对象内存块绑定,存在weak_ptr时无法实际释放内存
- Pimpl,Pointer to implementation:通过减少在类实现和类使用者之间的编译依赖来减少编译时间。(减少头文件中包含其他头文件的数量)
- unique_ptr: 独占所有权,符合抽象,析构函数和移动拷贝函数的定义需要在不完整类型实现之后
- shared_ptr:共享所有权,不符合抽象,运行时数据结构更大
// 原 header
class Widget() { //定义在头文件“widget.h”
public:Widget();…
private:std::string name;std::vector<double> data;Gadget g1, g2, g3; //Gadget是用户自定义的类型
};// 新 header
class Widget { //仍然在“widget.h”中
public:Widget();~Widget();Widget(Widget&& rhs); //只有声明Widget& operator=(Widget&& rhs);…private: //跟之前一样struct Impl;std::unique_ptr<Impl> pImpl;
};// 新 cpp
#include <string> //跟之前一样,仍然在“widget.cpp”中
…struct Widget::Impl { … }; //跟之前一样Widget::Widget() //跟之前一样
: pImpl(std::make_unique<Impl>())
{}Widget::~Widget() = default; //跟之前一样Widget::Widget(Widget&& rhs) = default; //这里定义
Widget& Widget::operator=(Widget&& rhs) = default;
Rvalue, Move & Forward
- **move:**无条件将实参转化为右值,不一定移动构造,const变量无法移动时只能拷贝构造
- **forward:**当且仅当实参为右值时,将其转化为右值
- move 和 forward 都是** cast 操作,运行期无代码**
- **通用引用:**T&&, auto&&,类型推导决定是左值引用还是右值引用
- 函数返回值,传参,最后一次使用:右值引用使用move,通用引用使用forward
- **返回值优化:**返回函数内的局部对象
- 使用move移动操作来返回
- 直接在返回值的内存中构建局部对象,消除拷贝操作
- 通用引用参数的函数重载,优先级高于大部分函数
- 完美转发的构造函数,non-const的优先级可能会大于拷贝构造和系统构造函数
- 重载通用引用:
- tag dispatch
template<typename T>
void logAndAdd(T&& name)
{logAndAddImpl(std::forward<T>(name),std::is_integral<typename std::remove_reference<T>::type>());
}template<typename T> //非整型实参:添加到全局数据结构中
void logAndAddImpl(T&& name, std::false_type) //译者注:高亮std::false_type
{auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}std::string nameFromIdx(int idx); //与条款26一样,整型实参:查找名字并用它调用logAndAdd
void logAndAddImpl(int idx, std::true_type) //译者注:高亮std::true_type
{logAndAdd(nameFromIdx(idx));
}
- enable_if 约束
class Person { //C++14
public:template<typename T,typename = std::enable_if_t< //这儿更少的代码!std::is_base_of<Person,std::decay_t<T> //还有这儿>::value> //还有这儿>explicit Person(T&& n);…
};
- 引用折叠(reference collapsing)。禁止你声明引用的引用,但是编译器会在特定的上下文中产生这些。当编译器生成引用的引用时,引用折叠指导下一步发生什么。
- 如果任一引用为左值引用,则结果为左值引用。否则(即,如果引用都是右值引用),结果为右值引用。
- 折叠发生在四种情况下:模板实例化,auto类型推导,typedef与别名声明的创建和使用,decltype。
- 通用引用就是在特定上下文的右值引用,上下文是通过类型推导区分左值还是右值,并且发生引用折叠的那些地方。
- **移动语义的缺陷:**某些情况不存在,速度不如复制,非noexcept移动不可用
- 完美转发(perfect forwarding)意味着我们不仅转发对象,我们还转发显著的特征:它们的类型,是左值还是右值,是const还是volatile。
- **完美转发失败:**花括号初始化,作为空指针的0或者NULL,仅有声明的整型static const数据成员,模板和重载函数的名字,位域。
template<typename T>
void fwd(T&& param) //接受任何实参
{f(std::forward<T>(param)); //转发给f
}template<typename... Ts>
void fwd(Ts&&... params) //接受任何实参
{f(std::forward<Ts>(params)...); //转发给f
}
- 重载拷贝左值,移动右值-》完美转发-》直接传值移动:
- 多一次额外的移动,适用移动开销低
- 左值:拷贝+移动
- 右值:移动+移动
- 实现简单,不用重载,不用模版
- 多一次额外的移动,适用移动开销低
class Widget { //方法1:对左值和右值重载
public:void addName(const std::string& newName){ names.push_back(newName); } // rvaluesvoid addName(std::string&& newName){ names.push_back(std::move(newName)); }
private:std::vector<std::string> names;
};class Widget { //方法2:使用通用引用
public:template<typename T>void addName(T&& newName){ names.push_back(std::forward<T>(newName)); }
};class Widget { //方法3:传值
public:void addName(std::string newName){ names.push_back(std::move(newName)); }
};
Lambda
lambdas 和闭包类存在于编译期,闭包存在于运行时。
- **按引用捕获:**捕获的变量超出生命周期,导致悬空引用
- **按值捕获【默认】:**捕获指针,导致悬空引用;无法捕捉成员变量,只能捕捉this指针;无法捕捉static变量
- **初始化捕获,可以捕获移动对象【移动捕获】:**c++11可以使用bind来模拟捕获移动对象
auto func = [pw = std::move(pw)] //使用std::move(pw)初始化闭包数据成员{ return pw->isValidated()&& pw->isArchived(); };auto func = [pw = std::make_unique<Widget>()] //使用调用make_unique得到的结果{ return pw->isValidated() //初始化闭包数据成员&& pw->isArchived(); };
- **泛型捕获:**捕获auto&&,C++11可以使用bind的完美转发来模拟泛型捕获
auto f = [](auto&& param){return func(std::forward<decltype(param)>(param));};auto f = [](auto&&... param){return func(std::forward<decltype(param)>(param)...);};
- 使用lambda取代bind:
// lambda
auto setSoundL = [](Sound s) {using namespace std::chrono;using namespace std::literals; //对于C++14后缀setAlarm(steady_clock::now() + 1h, //C++14写法,但是含义同上s,30s);};// bind
using SetAlarm3ParamType = void(*)(Time t, Sound s, Duration d);auto setSoundB = std::bind(static_cast<SetAlarm3ParamType>(setAlarm), // 无法处理类型重载,需要显式制定类型std::bind(std::plus<>(), steady_clock::now(), 1h), // 需要推迟表达式求值,避免在调用实际函数前求值_1,30s);
Concurrency API
Concurrency support library (since C++11) - cppreference.com
concurrency library
- :Mutual exclusion
- lock_guard,unique_lock
- <condition_variable>
atomic & mutex
- **atomic:**原子操作,一个CPU指令实现
- **mutex:**互斥锁,可由atomic实现加锁解锁操作,未拿到锁需要陷入内核完成线程挂起
- **自旋锁:**可由atomic实现,未拿到锁不会挂起线程,效率高于互斥锁
atomic & volatile
- std::atomic用在并发编程中,对访问特殊内存没用。
- 可以限制编译器或底层硬件对代码进行重排和乱序执行,在不使用互斥锁时,避免并发编程时的数据竞争问题
- volatile用于访问特殊内存,对并发编程没用。
- 内存映射IO时,编译器不会优化重复读写,冗余访问,无用存储等代码
thread, pthread & cpu cores
- 硬件线程:
- 物理CPU个数:个人电脑1个插槽,服务器2个插槽
- 物理CPU核心数【物理CPU的芯片组数量】:双核,四核,八核
- 逻辑核=虚拟核=硬件线程【超线程,同步多线程技术】:8核16线程
- 软件线程:
- 系统线程,操作系统线程,操作系统调度到硬件线程上执行的线程
- pthread(POSIX thread),windows thread
- std::thread:
- C++对象,软件线程的句柄
- joinable:正在运行
- unjoinable:
- 默认构造
- 已被移动
- join:运行完毕
- detach:与软件线程之间的连接关系被打断
thread & future
- 基于线程异步执行:std::thread t(doAsyncWork);
- 不能直接访问异步执行的结果,如果执行函数有异常抛出,代码会终止执行。
- 自己处理线程耗尽、资源超额、负载均衡、平台适配等问题
- 可以访问pthread api,使用thread pool
- 基于任务异步执行:auto fut = std::async(doAsyncWork);
- 可以获取异步执行的结果和异常
- 标准库处理线程耗尽、资源超额、负载均衡、平台适配等问题
- 默认std::launch::async | std::launch::deferred, 不保证执行,异步执行, std::launch::async强制异步执行
thread disconstruction
析构joinable的thread会导致程序终止。
使用RAII(“Resource Acquisition Is Initialization,资源获得即初始化)包装thread,在析构时进行join。
class ThreadRAII {
public:enum class DtorAction { join, detach };ThreadRAII(std::thread&& t, DtorAction a): action(a), t(std::move(t)) {}~ThreadRAII(){if (t.joinable()) {if (action == DtorAction::join) {t.join();} else {t.detach();}}}std::thread& get() { return t; }private:DtorAction action;std::thread t;
};
future disconstruction
析构future会导致隐式的join或隐式的detach:
- 引用了强制异步任务的共享状态的最后一个future,隐式join,在异步执行完成之前阻塞
- 其他future,隐式detach
thread communication: condition variable,atomic & future
- **condition variable + mutex:**需要结合互斥锁,且需要重复验证避免虚假唤醒
std::condition_variable cv; //事件的条件变量
std::mutex m; //配合cv使用的mutex
… //检测事件
cv.notify_one(); //通知反应任务
… //反应的准备工作
{ //开启关键部分std::unique_lock<std::mutex> lk(m); //锁住互斥锁cv.wait(lk); //释放锁后等待通知,通知后再次加锁… //对事件进行反应(m已经上锁)
} //关闭关键部分;通过lk的析构函数解锁m
… //继续反应动作(m现在未上锁)
- **atomic:**基于轮询,而不是阻塞
std::atomic<bool> flag(false); //共享的flag
… //检测某个事件
flag = true; //告诉反应线程
… //准备作出反应
while (!flag); //等待事件
… //对事件作出反应
- **future:**使用了堆内存存储共享状态,只能使用一次通信
std::promise<void> p; //通信信道的promise
… //检测某个事件
p.set_value(); //通知反应任务
… //准备作出反应
p.get_future().wait(); //等待对应于p的那个future
… //对事件作出反应
相关文章:

读书笔记:《Effective Modern C++(C++14)》
Effective Modern C(C14) GitHub - CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C》- 完成翻译 Deducing Types 模版类型推导: 引用,const,volatile被忽略数组名和函数名退化为指针通用引用&#…...

PCL 点云加权均值收缩
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 受到之前Matlab 加权均值质心计算(WMN)的启发,我们在计算每个点的加权质心时可以很容易的发现,他们这些点会受到周围邻近点密度的影响,最后会收缩到某一个区域,那么这个区域也必定是我们比较感兴趣的一些点,…...

计算机毕业设计 基于协同推荐的白酒销售管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...

React-hook-form-mui(五):包含内嵌表单元素的表单
前言 在上一篇文章中,我们介绍了react-hook-form-mui如何与与后端数据联调。在实际项目中,从后端获取的数据可能是复杂的数据对象,本文将介绍,如何通过react-hook-form-mui实现一个包含内嵌表单元素的表单 Demo 以下代码实现了…...

【内网安全】搭建网络拓扑,CS内网横向移动实验
文章目录 搭建网络拓扑 ☁环境CS搭建,木马生成上传一句话,获取WebShellCS上线reGeorg搭建代理,访问内网域控IIS提权信息收集横向移动 实验拓扑结构如下: 搭建网络拓扑 ☁ 环境 **攻击者win10地址:**192.168.8.3 dmz win7地址&…...

1、输入一行字符,分别统计出其中的英文字母、空格、数字和其他字符的个数。
1、输入一行字符,分别统计出其中的英文字母、空格、数字和其他字符的个数。 int main(){char str[N];int letter 0,space 0,digit 0, others 0;printf("请输入一行字符:");gets(str);for(int i0;str[i]!\0;i){if((a<str[i] && …...

戴尔科技推出全新96核Precision 7875塔式工作站
工作站行业一直是快节奏且充满惊喜的。在过去25年中,戴尔Precision一直处于行业前沿,帮助创作者、工程师、建筑师、研究人员等将想法变为现实,并对整个世界产生影响。工作站所发挥的作用至关重要,被视为化不可能为可能的必要工具。如今,人工智能(AI)和生成式AI(GenAI)的浪潮正在…...

论文阅读——DINOv
首先是关于给了提示然后做分割的一些方法的总结: 左边一列是prompt类型,右边一列是使用各个类型的prompt的模型。这些模型有分为两大类:Generic和Refer,通用分割和参考分割。Generic seg 是分割和提示语义概念一样的所有的物体&am…...

JOSEF电流继电器 DL-33 整定范围0.5-2A 柜内安装板前接线
系列型号: DL-31电流继电器; DL-32电流继电器; DL-33电流继电器; DL-34电流继电器; 一、用途 DL-30系列电流继电器用于电机保护、变压器保护和输电线的过负荷和短路保护线路中,作为起动元件。 二、结构和原理 继电器系电磁式,瞬时动作…...

RCE绕过
1.[SCTF 2021]rceme 总结下获取disabled_funciton的方式 1.phpinfo() 2.var_dump(ini_get(“disable_functions”)); 3.var_dump(get_cfg_var(“disable_functions”)); 其他的 var_dump(get_cfg_var(“open_basedir”)); var_dump(ini_get_all()); <?php if(isset($_POS…...

Qt应用开发--国产工业开发板全志T113-i的部署教程
Qt在工业上的使用场景包括工业自动化、嵌入式系统、汽车行业、航空航天、医疗设备、制造业和物联网应用。Qt被用来开发工业设备的用户界面、控制系统、嵌入式应用和其他工业应用,因其跨平台性和丰富的功能而备受青睐。 Qt能够为工业领域带来什么好处: -…...

css 常用动画效果
css 常用动画效果 文章目录 css 常用动画效果1.上下运动动画2.宽度变化动画 1.上下运动动画 <div class"box"><div class"item"></div> </div>css .box {position: relative; }.item {position: absolute;width: 50px;height: 50…...

【读书笔记】微习惯
周日晚上尝试速读一本书《微习惯》,共七章看了下目录结构并不复杂,计划每章7-8分钟读完, 从20:15-21:00。读的时候,订下闹钟,催促着自己的进度。边读边记了一些要点和微信读书里面的划线。 第六章实践内容最为丰富&…...

Oracle SQL优化
1、书写顺序和执行顺序 在Oracle SQL中,查询的书写顺序和执行顺序是不同的。 1.1SQL书写顺序如下: SELECTFROMWHEREGROUP BYHAVINGORDER BY 1.2 SQL执行顺序 FROM:数据源被确定,表连接操作也在此步骤完成。 WHERE:对…...

C++实现ATM取款机
C实现ATM取款机 代码:https://mbd.pub/o/bread/ZZeZk5Zp 1.任务描述 要求:设计一个程序,当输入给定的卡号和密码(初始卡号和密码为123456) 时,系统 能登录 ATM 取款机系统,用户可以按照以下规则进行: 查询…...

【数电笔记】11-最小项(逻辑函数的表示方法及其转换)
目录 说明: 逻辑函数的建立 1. 分析逻辑问题,建立逻辑函数的真值表 2. 根据真值表写出逻辑式 3. 画逻辑图 逻辑函数的表示 1. 逻辑表达式的常见表示形式与转换 2. 逻辑函数的标准表达式 (1)最小项的定义 (2&am…...

Gradio库的安装和使用教程
目录 一、Gradio库的安装 二、Gradio的使用 1、导入Gradio库 2、创建Gradio接口 3、添加接口到Gradio应用 4、处理用户输入和模型输出 5、关闭Gradio应用界面 三、Gradio的高级用法 1、多语言支持 2、自定义输入和输出格式 3、模型版本控制 4、集成第三方库和API …...

【BLE基础知识】--Slave latency设置流程及空中包解析
1、Slave latency基本概念 当BLE从设备对耗电量要求较高时,若需要节省耗电量,则可以通过设置Slave Latency参数来减少BLE从设备的耗电。 Slave Latency:允许Slave(从设备)在没有数据要发的情况下,跳过一定…...

数据结构之堆排序以及Top-k问题详细解析
个人主页:点我进入主页 专栏分类:C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 欢迎大家点赞,评论,收藏。 一起努力 目录 1.前言 2.堆排序 2.1降序排序 2.2时间复杂…...

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO
ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO 概述 上节 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 讲述了如何通过网页控制一个 GPIO。本节实现在网页上控制多个 GPIO。 示例解析 前端设计 前端代码建立了四个 GPIO,如下死 GPIO 2 在前端的…...

说一说MySQL中的锁机制
说一说MySQL中的锁机制 按粒度大小从大到小分为 全局锁 全局锁 全局锁是对整个数据库的锁,最常用的全局锁就是读写锁 读锁 阻止其他用户更新数据,允许其他用户读数据写锁 阻止其他用户更新和读数据 修改一些大量的数据,并且不希望其他用户…...

C++笔试训练day_1
文章目录 选择题编程题 选择题 编程题 #include <iostream> #include <algorithm> #include <vector>using namespace std;int main() {int n 0;cin >> n;vector<int> v;v.resize(3 * n);int x 0;for(int i 0; i < v.size(); i){cin >&…...

详解Spring对Mybatis等持久化框架的整合
😉😉 学习交流群: ✅✅1:这是孙哥suns给大家的福利! ✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 🥭🥭3:QQ群:583783…...

[Electron] 将应用打包成供Ubuntu、Debian平台下安装的deb包
在使用 electron-packager 工具输出 linux 平台的 electron app 后,可以使用 electron-installer-debian 工具把 app 打包成供Ubuntu平台下安装的 debian 包。 electron-installer-debian是一个用于创建 Debian Linux(.deb)安装包的开发工…...

7.24 SpringBoot项目实战【审核评论】
文章目录 前言一、编写控制器二、编写服务层三、Postman测试前言 我们在 上文 7.23 已经实现了 评论 功能,本文我们继续SpringBoot项目实战 审核评论 功能。逻辑如下: 一是判断管理员权限,关于角色权限校验 在 7.5 和 7.6 分别基于 拦截器Interceptor 和 切面AOP 都实现过…...

Java实现动态加载的逻辑
日常工作中我们经常遇到这样的场景,某某些逻辑特别不稳定,随时根据线上实际情况做调整,比如商品里的评分逻辑,比如规则引擎里的规则。 常见的可选方案有: JDK自带的ScriptEngine 使用groovy,如GroovyClassLoader、Gro…...

数据库的设计规范
文章目录 第一范式(1NF):列不可再分 第二范式 (2NF):所有非主键字段,都必须 完全依赖主键,不能部分依赖 第三范式(3NF):所有非主键字段不能依赖于…...

正则表达式从放弃到入门(2):grep命令详解
正则表达式从放弃到入门(2):grep命令详解 总结 本博文转载自 这是一篇”正则表达式”扫盲贴,如果你还不理解什么是正则表达式,看这篇文章就对了。 如果你是一个新手,请从头阅读这篇文章,如果你…...

用Java写一个王者荣耀游戏
目录 sxt包 Background Bullet Champion ChampionDaji GameFrame GameObject Minion MinionBlue MinionRed Turret TurretBlue TurretRed beast包 Bear Beast Bird BlueBuff RedBuff Wolf Xiyi 打开Eclipse创建图片中的几个包 sxt包 Background package sxt;…...

基于SSM的新闻网站浏览管理实现与设计
基于ssm的新闻网站浏览管理实现与设计 摘要:在大数据时代下,科技与技术日渐发达的时代,人们不再局限于只获取自己身边的信息,而是对全球信息获取量也日渐提高,网络正是打开这新世纪大门的钥匙。在传统方式下ÿ…...