读书笔记:《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 在前端的…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...