实用指南:C++11(二)

news/2026/1/17 18:42:38/文章来源:https://www.cnblogs.com/gccbuaa/p/19496940

目录

一、可变参数模板

1、基本语法及原理

2、包扩展

3、emplace 系列接口

二、新的类功能

1、默认生成的移动构造和移动赋值

2、成员变量在声明时给缺省值

3、default 和 delete

三、可调用对象

1、lambda 表达式

1.1、lambda 表达式的语法

1.2、捕捉列表

1.3、lambda 表达式的应用

1.4、lambda 表达式的原理

2、包装器 function

3、函数适配器 bind

后记


前言

  上篇我们讲完了完美转发,但是C++11要讲的内容很多,所以我们就将剩下的内容放到了这篇来讲,准备接受知识的醍醐灌顶吧孩子们。

一、可变参数模板

1、基本语法及原理

  从C++11以后,就出现了可变参数模板,也就是说出现了支持可变参数的函数模板和类模板。可变参数被称为参数包,其实就是把参数打包到了一块取了个名字叫做参数包,参数包分为两种:函数参数包和模板参数包。

template  void Func(Args... args) {}
template  void Func(Args&... args) {}
template  void Func(Args&&... args) {}

  我们用省略号来表示一个模板参数或函数参数是一个包,在模板参数列表中,class...或 typename...指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟...指出 接下来表示零或多个形参对象列表;函数参数包可以用左值引用或右值引用表示,跟前面普通模板 一样,每个参数实例化时遵循引用折叠规则。

  我们可以使用 sizeof... 来计算参数包中有几个参数。如下:

template 
void Print(Args&&... args)
{cout << sizeof...(args) << endl;
}

2、包扩展

  参数包其实只是我们看到的样子,在底层编译时包会扩展成正常的多参数的样子。当扩展一个包时,我们还要提供用于每个扩展元素的模式,扩展一个包就是将它分解为构成的元素,对每个元 素应用模式,获得扩展后的列表。我们通过在模式的右边放一个省略号(...)来触发扩展操作。

  我们来看看底层的原理(包扩展会在编译时扩展完毕):

  可变参数模板参数包的处理方式有两种:一种是直接在调用处展开参数包(最常见的方式,一会要讲的emplace接口就是使用的这种方法),第二种是递归分解(需要终止函数,上面图中展示的方式就是递归分解)。

3、emplace 系列接口

  C++11以后STL新增了 emplace 系列接口,模板参数均为可变参数。

template  void emplace_back (Args&&... args);
template  iterator emplace (const_iterator position, Args&&... args);

  emplace 系列接口兼容我们平常使用的 push 和 insert 系列函数的功能,并且更改了一下插入的逻辑,大大提高了插入的效率。那具体有什么不同呢?

  insert 和 push 系列函数是先在容器外部构造临时对象,然后再将构造好的临时对象移动或拷贝到容器内,比如有以下例子:

vector vec;
vec.push_back("Hello");
//这里就是先用const char*类型的值调用string构造函数构造出一个string类的临时对象
//然后将这个对象通过移动构造或拷贝构造转移到容器vector中去

  但是 emplace 系列的插入不会产生临时对象,而是直接在容器内部进行构造,也就是说少了一次移动的过程(如果移动时采用的是移动构造则花销不大,如果是拷贝构造花销不就大了嘛)。没有对比大家可能看不出来区别,我给大家画张图:

  所以,emplace 的插入接口确实比 push 和 insert 接口更高效,所以以后我们尽量多使用 emplace 的插入接口。由于 emplace 插入是可变参数,所以他是多参数直接传入即可,不需要使用列表初始化,但是当存储值类似于 pair 类型时,push 插入需要使用列表初始化,以 push_back 和 emplace_back 为例:

list> lt;
lt.emplace_back("苹果", 3);//不需要列表初始化
lt.push_back({ "苹果", 3 });//需要列表初始化

二、新的类功能

1、默认生成的移动构造和移动赋值

  原来C++类中,有6个默认成员函数:构造函数/析构函数/拷贝构造函数/拷贝赋值重载/取地址重 载/const 取地址重载,最后重要的是前4个,后两个用处不大,默认成员函数就是我们不写编译器 会默认生成。C++11 新增了两个默认成员函数,移动构造函数和移动赋值运算符重载。

如果你没自己实现移动构造函数,且析构函数 、拷贝构造、拷贝赋值重载都没有实现,那么编译器会自动生成一个默认移动构造(一般这四个默认成员函数是绑定的,要写都写,要不写都不写,因为几乎都是需要深拷贝时才需要自己写)。默认生成的移动构造函数,对于内置类型成员会进行浅拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

  如果你没有自己实现移动赋值重载函数,且析构函数 、拷贝构造、拷贝赋值重载都没有实现,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会进行浅拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)

如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

2、成员变量在声明时给缺省值

  没错,这个玩意也是C++11才出现的,记得我们说过,如果没有在构造函数的初始化列表进行显示初始化,就需要在成员变量声明的地方给上缺省值(这里的初始化列表是我们在类和对象那里讲的一种语法,而不是C++11加入的初始化列表类型)。忘记的去看我的类与对象(下)(一)那篇文章。

3、default 和 delete

  C++11 就可以更好地控制我们的默认生成的成员函数了。

  default 关键字可以让编译器强制生成一个默认成员函数。比如,由于我们自己写了拷贝构造,系统就不会默认生成移动构造了,但是我们可以使用 default 关键字强制生成该函数。用法就是写一个函数声明,在后面加上 = default,我们以移动构造为例:

class Person
{Person(const Person& per)//拷贝构造{...}Person(Person&& per) = default;....
}

  delete 关键字用法和 default 相同,但是作用和 default 作用正好相反,他的作用是限制默认成员函数的默认生成,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。看用法:

Person(const Person& per) = delete;

三、可调用对象

  首先声明一下,可调用对象并不是 C++11 才出现的概念,在 C++11 之前就已经出现了。我们先来介绍一下什么是可调用对象。

在 C++ 中,可调用对象并不一定是对象,它是一个比较宽泛的概念,凡是可以用 () 调用的对象在 C++ 中都叫做可调用对象。比如函数指针,函数对象(比如仿函数),普通函数都属于可调用对象。可以看到,这里的函数指针和普通函数并不属于对象,但他们依然属于可调用对象范围内。

  所以一般传可调用对象的位置都可以传普通函数,传普通函数的位置也几乎都可以传可调用对象,因为编译器不会管你是不是对象,只会管你是不是可调用的。比如,传仿函数的位置也可以传普通函数(因为普通函数可以隐式转换为仿函数调用的形式)。比如以下例子:

#include 
#include 
// 普通函数
bool compare(int a, int b) { return a > b; }
// 仿函数
struct Comparator {bool operator()(int a, int b) const { return a < b; }
};
int main() {std::vector v = {3, 1, 4, 1, 5};// 传递普通函数std::sort(v.begin(), v.end(), compare);// 传递仿函数std::sort(v.begin(), v.end(), Comparator());
}

  有人会问,既然可调用对象不是 C++11 才出现的,为什么要在这里讲,首先是因为 C++11 新加入了好几个可调用对象,其次是因为我们有时传参会传入可调用对象(主要是为了告诉大家函数也属于可调用对象,需要传可调用对象的地方也可以传普通函数),我们接下来一一讲解新加入的可调用对象。

1、lambda 表达式

  是的,你没看错,就是 python 里的 lambda 表达式,听说是抄的,甚至将这个语法挪过来都没咋改,这样就让我们使用这个语法的时候看起来很违和,没错,这个表达式的风格很不C++。

1.1、lambda 表达式的语法

lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是他可以定义在函数内部。lambda 表达式在使用层面上没有类型名(具体看原理),所以我们一般用 auto 或模板参数定义的对象(比如仿函数对象,所以仿函数对象可以用lambda表达式代替)来接收 lambda 对象。

来看看 lambda 表达式的格式:[捕捉列表] (参数列表)->返回类型 { 函数体 }

捕捉列表里的内容可以空着,[] 不可以省略

参数列表如果不需要传参可以连着 () 一起省略

->返回类型可以省略不写,因为编译器会自行推倒

函数体可以空着,但是这里的 {} 不可以省略

所以一个最简单的 lambda 表达式是这样的 [ ]{ }

  直接来看例子,捕捉列表先空着,我们一会再讲,这里直接空着就行:

auto add1 = [](int x, int y)->int {return x + y; };
//不过一般会省略->返回类型
//auto add1 = [](int x, int y) {return x + y; };

1.2、捕捉列表

  lambda 表达式默认只能使用函数体中定义的变量或函数参数中的变量,如果需要使用外层作用域的变量就需要进行捕捉。

这里有三种捕捉方式:

1、在捕捉列表中显式地进行传值捕捉和引用捕捉,捕捉多个变量要用逗号分割,如下:[x, y, &z] 表示的就是捕捉 x 和 y 的值,对 z 引用捕捉。

2、隐式捕捉:在捕捉列表里只写一个 = 就表示隐式传值捕捉(像这样:[ = ]),只写一个 & 就表示隐式引用捕捉(像这样:[ & ])。隐式捕捉后在函数体里默认可以使用所以可以捕捉的参数,但是并不是将这些参数全都捕捉,而是使用哪个捕捉哪个。

3、混合捕捉:就是上面的混起来用,比如 [ = , &x] 就表示除了x是引用捕捉,其他都是传值捕捉。

  lambda 捕捉列表可以捕捉当前作用域内在 lambda 表达式之前定义的变量,但是不能捕捉静态局部变量和全局变量(因为这两种变量不需要捕捉就可以直接使用)。

  传值捕捉默认不可以在函数体内部修改参数的值,引用捕捉可以修改且会影响外面该参数的值。这时我们想要修改传值捕捉得到的参数的值,就可以使用 mutable,将它加在参数列表后面表示取消传值捕捉参数的常性,但是在函数体里修改该参数仍不会影响外面的该参数(相当于将这个参数拷贝了一份)。使用 mutable 修饰后,参数列表不可省略(即使参数列表为空,也要带上 ()),用法如下:

auto func7 = [=]()mutable
{a++;b++;c++;d++;return a + b + c + d;
};

1.3、lambda 表达式的应用

  我们以 sort 函数为例,使用 lambda 表达式:

sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price;});

  往常,我们的第三个参数应该传一个仿函数(函数对象),由于 lambda 表达式也是可调用对象,所以自然也可以替代仿函数的位置。

  注意:lambda 表达式是一个匿名对象,一个可调用的对象,可以把它理解为仿函数。

1.4、lambda 表达式的原理

  我们之前说过,将 lambda 表达式当作仿函数理解就可以,其实 lambda 表达式底层实现就是用的仿函数对象,lambda 表达式只是表象,当我们写了这个玩意以后,其实在底层来看我们就是写了一个仿函数。

  不同的仿函数类名不同,由于lambda表达式底层也是仿函数,所以在底层,每个lambda表达式都有自己的类名,编译器生成的类名也很简单,就是 lambda 加上一串数字和字母。

  捕捉列表的本质是底层仿函数类的成员变量,也就是说捕捉列表的变量都是 lambda 类构造函数的实参,对于隐式捕捉,当然不是全都捕捉进捕捉列表,而是使用哪个捕捉哪个。

2、包装器 function

  首先我们先来看 std::function ,他也是一个可调用对象(用 () 调用的本质是 () 被进行了函数重载)。

  std::function 本质是一个类模板,也是一个包装器。那为什么它被称作包装器呢,其实是因为它的实例化对象能够将其他的可调用对象(比如函数指针,函数,lambda表达式,bind表达式等)包装起来,存储的可调用对象被称为 function 的目标。如果 function 不含有目标,则称它为空。调用空 std::function 的目标导致抛出 std::bad_function_call 异常。

  那为什么将其他的可调用对象包装起来呢?其实是因为其他可调用对象的类型五花八门,我们就可以把他们包装起来,统一成一个类型,这就是 function 包装器的妙处,这时声明可调用对象的类型就方便了。

  使用 function 需要包上头文件 <functional>,我们来看看它的模板:

//没有定义时
template
class function;
//有定义时
template
class function;

  是的,没错,function的参数是可变参数,这里的 Ret 为返回值,可变参数处传你要用的参数类型。只看模板大家可能看不懂,我们给大家看个例子:

class Plus
{
public:Plus(int n = 10):_n(n){ }static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return (a + b) * _n;}
private:int _n;
};
int main()
{function f = &Plus::plusi;cout << f(3, 2) << endl;
}

下面有两个要注意的点:

1、当要包装的是静态成员函数时,一定要指明类域并且在前面加上&(成员函数都要指明类域并加上&),如下:

class Plus
{
public:Plus(int n = 10):_n(n){ }static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return (a + b) * _n;}
private:int _n;
};
int main()
{function f = &Plus::plusi;cout << f(3, 2) << endl;
}

2、当要包装的是普通成员函数时,要记得普通成员函数的第一个参数是那个隐藏的 this 指针。

int main()
{function f1 = &Plus::plusd;//第一个参数this指针Plus pa;cout << f1(&pa, 3.2, 3.3) << endl;function f2 = &Plus::plusd;//第一个参数可以传对象Plus pb;cout << f2(pb, 4.3, 5.4) << endl;function f3 = &Plus::plusd;//第一个对象可以传对象的引用Plus pc;cout << f3(pc, 4.3, 5.4) << endl;/*function f4 = &Plus::plusd;Plus pd;cout << f4(pd, 4.3, 5.4) << endl;*///但是不能传const对象,因为this指针为const类型的话就是const成员函数了,要单独有一个const版本的成员函数
}

  要想最后一个运行成功,要添加一个成员函数:

class Plus
{
public:double plust(double a, double b) const//要匹配const成员函数{return (a + b) * _n;}
private:int _n;
};
int main()
{function f4 = &Plus::plust;Plus pd;cout << f4(pd, 4.3, 5.4) << endl;
}

  同样也可以使用右值,如下:

function f5 = &Plus::plusd;Plus pc;cout << f5(move(pc), 4.3, 5.4) << endl;cout << f5(Plus(), 4.3, 5.4) << endl;

3、函数适配器 bind

  前面我们讲的 function 是类模板,而这里我们讲的 bind 则是函数模板,他也是一个可调用对象。他是一个函数适配器(也可以看作合成器),他并不是一个对象。

template 
bind (Fn&& fn, Args&&... args);//Fn传可调用对象

  bind 的作用是把可调用对象包装后返回一个可调用对象,这个可调用对象可以用 auto 对象和合成器 function 对象接收,毕竟合成器 function 的作用不就是接收各种可调用对象嘛。它的头文件也是 <functional>。

  调用bind的一般形式: auto newCallable = bind(callable,arg_list); 其中 newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的 callable 的参数。当我们调用 newCallable时,newCallable会调用 callable,并传给它 arg_list 中的参数。

  arg_list 中的参数可能包含形如 _n 的名字,其中n是一个整数,这些参数是占位符,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的位置。数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2为第二个参数,以此类推。_1/_2/_3....这些占位符都放在一个在 placeholders 的命名空间中。

  只看这些定义大家可能看不懂,下面来给大家讲一下:

  bind 很常用的作用有两个,一是改变可调用对象参数的位置,把参数按照自己的想法调换顺序,如下:

//可以直接展开命名空间 using namespace placeholders,但是这里我们只用三个参数做演示,就不全展开了
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int a, int b)
{return (a - b) * 10;
}
int SubX(int a, int b, int c)
{return (a - b - c) * 10;
}
int main()
{cout << Sub(10, 5) << endl;auto b1 = bind(Sub, _2, _1);cout << b1(10, 5) << endl;cout << SubX(10, 2, 3) << endl;auto b2 = bind(SubX, _2, _3, _1);cout << b2(10, 2, 3) << endl;return 0;
}

运行结果如下:

那么看到这里大家可能还是不太明白原理,那我给大家画个图:

第二个作用是改变参数数量,也就是 bind 可以让原可调用对象的某个参数的值固定(把一个值与某个参数绑定)从而达到减少参数的目的,如下:

int SubX(int a, int b, int c)
{return (a - b - c) * 10;
}
int main()
{auto b = bind(SubX, _2, 10, _1);cout << b(5, 35) << endl;
}

运行结果如下:

还是画图分析一下:

  当然,我么也可以用 function 对象来接收 bind 的返回值 :

class Plus
{
public:double plusd(double a, double b){return a + b;}
};
int main()
{//和function一样,成员函数指明类域并加上&function f = bind(&Plus::plusd, Plus(), _1, _2);cout << f(1.1, 1.1) << endl;
}

后记

  C++11 添加的内容重要的大部分都讲完了,我还要讲一个智能指针,但是篇幅限制,我要单独放到一篇文章来讲,今天的内容就到这里吧,感谢大家支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1174463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

强烈安利10个AI论文工具,专科生轻松搞定论文写作!

强烈安利10个AI论文工具&#xff0c;专科生轻松搞定论文写作&#xff01; AI工具如何让论文写作不再难 对于专科生来说&#xff0c;论文写作一直是一个令人头疼的难题。从选题到开题&#xff0c;从撰写到降重&#xff0c;每一个环节都可能成为阻碍。而如今&#xff0c;随着AI技…

GNSS位移监测在单北斗变形监测一体机中的应用与发展分析

GNSS位移监测技术在单北斗变形监测一体机中的应用日益广泛&#xff0c;涵盖了从基础设施维护到自然灾害预警的多个领域。单北斗系统通过高精度定位&#xff0c;实时获取重要的位移数据&#xff0c;帮助工程师及时判断结构安全情况。特别是在桥梁和地质灾害监测方面&#xff0c;…

AGC060C Large Heap 题解 / 计数 dp

题目传送门:AGC060C Large Heap。 首先这个东西完全就是一个满二叉树的堆,相当于权值顺序要为一个拓扑,且 \(U\) 在 \(V\) 前面。 而 \(U,V\) 在 \(A+1\) 与 \(B+1\) 层的最左和左右端点。 那么 \(U,V\) 分别是两条…

2026年AI艺术码二维码生成器推荐榜单:探寻最佳选择

在2026年&#xff0c;AI艺术码二维码生成器逐渐成为创意表达的重要工具。这些生成器帮助用户将各类内容转化为二维码&#xff0c;使艺术作品更加吸引人。以码上游、QRCodeMonkey和QR Code Generator PRO为代表&#xff0c;它们各有特色&#xff0c;能满足不同用户的需求。 个…

2026年1月二手宽体车公司评测报告:二手宽体车公司选择指南! - 品牌鉴赏师

一、开篇引言 近年来,我国二手宽体车市场尤其是矿用宽体车领域发展态势强劲,据太平洋汽车百科2025年行业分析报告显示,受益于矿山智能化建设推进及工程运输领域需求释放,二手宽体车市场规模年均增速保持在12%以上,…

2026年1月二手宽体车公司评测报告:二手宽体车公司选择指南! - 品牌鉴赏师

一、开篇引言 近年来,我国二手宽体车市场尤其是矿用宽体车领域发展态势强劲,据太平洋汽车百科2025年行业分析报告显示,受益于矿山智能化建设推进及工程运输领域需求释放,二手宽体车市场规模年均增速保持在12%以上,…

如何应对启动错误:一步步解决错误代码0xc0000001分享

当您尝试开启电脑时&#xff0c;面对的错误代码0xc0000001可能让您感到困惑和无助。这个错误通常涉及到系统文件的问题或硬件故障&#xff0c;但幸运的是&#xff0c;有多种方法可以尝试解决这个问题。本文将提供一个详尽的步骤指南&#xff0c;帮助您从错误中恢复出来&#xf…

CVE-2025-68645 Zimbra Collaboration Suite 本地文件包含漏洞分析

&#x1f525; CVE-2025-68645: Zimbra Collaboration Suite — 本地文件包含 (LFI) 漏洞分析 &#x1f4d6; 项目概述 本项目详细解析了编号为 CVE-2025-68645 的安全漏洞。该漏洞存在于 Zimbra Collaboration Suite (ZCS) 中&#xff0c;是一个无需身份验证即可远程利用的本地…

CVE-2025-68645 Zimbra Collaboration Suite 本地文件包含漏洞分析

&#x1f525; CVE-2025-68645: Zimbra Collaboration Suite — 本地文件包含 (LFI) 漏洞分析 &#x1f4d6; 项目概述 本项目详细解析了编号为 CVE-2025-68645 的安全漏洞。该漏洞存在于 Zimbra Collaboration Suite (ZCS) 中&#xff0c;是一个无需身份验证即可远程利用的本地…

AI 主导研发项目溢价评估与工作量核算的思考?

AI 主导研发&#xff08;仅 5% 人工投入&#xff09;的溢价评估 和 甲方评估工作量并推动乙方接受 的问题&#xff0c;核心逻辑是&#xff1a;溢价≠单纯按人工成本比例降低&#xff0c;而是基于「AI 提效带来的价值增量」和「人工投入的核心价值」双向定价&#xff1b;甲方评估…

深入解析:Spring AI 2.x 发布:全面拥抱 Java 21,Redis 史诗级增强

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

RustFS:基于Rust的高性能分布式对象存储,重新定义数据存储新标准!

RustFS:基于Rust的高性能分布式对象存储,重新定义数据存储新标准!RustFS 是一个基于 Rust 语言开发的开源分布式对象存储系统,性能比 MinIO 快 2.3 倍,完全兼容 S3 协议,为企业级应用提供高性能、高可靠性的数据…

哈希分分预测系统 + Python Worker + Web 仪表盘”小系统(PHP + MySQL)

一个可上线运行的“数据抓取 Python Worker Web 仪表盘”小系统&#xff08;PHP MySQL&#xff09;说明&#xff1a;本文主要分享工程实现方法&#xff0c;用于学习“定时抓取、入库、异步 Worker、可视化仪表盘、权限管理、运维守护”等技术点。文中展示的“预测/命中率”仅…

导师严选10个AI论文工具,研究生高效写作必备!

导师严选10个AI论文工具&#xff0c;研究生高效写作必备&#xff01; AI 工具如何助力研究生高效论文写作 在当前学术研究日益激烈的环境下&#xff0c;研究生们面临着从选题、开题到撰写、降重的多重挑战。而 AI 工具的出现&#xff0c;为这一过程注入了新的活力。无论是降低 …

ppo怎么知道好动作不好动作,我现在这个环境完成任务得到回报50个动作可能就三个是对的

这正是强化学习最核心的挑战&#xff0c;被称为“信用分配问题”。你描述的场景——在50个动作中只有3个是关键的&#xff0c;就像大海捞针一样。如果只看最后的总分&#xff08;比如赢了1&#xff0c;输了-1&#xff09;&#xff0c;智能体确实很难知道是哪3个动作立了大功。为…

如何使用 httpx + SQLAlchemy 异步高效写入上亿级图片链接与MD5到 PostgreSQL

文章目录一、引言&#xff1a;为什么需要异步批量写入&#xff1f;二、技术选型深度解析2.1 为什么选择 httpx&#xff1f;2.2 为什么必须用 SQLAlchemy 2.0 异步&#xff1f;2.3 为什么不用 ORM 对象&#xff0c;而用原生 SQL&#xff1f;2.4 实践建议三、数据库表结构设计&am…

健康宣教二维码是什么?主要有哪些创新优势?

健康宣教二维码是一种创新的健康教育方式&#xff0c;利用二维码技术为患者提供便捷的健康信息获取渠道。通过扫描二维码&#xff0c;患者可以立刻访问丰富的健康知识&#xff0c;比如疾病管理、饮食指导和日常保健等。这种方式不仅提高了信息的覆盖面&#xff0c;还增加了患者…

模组的功耗说明,新手不可不知的功耗常识

在设计低功耗设备时,模组的电流消耗往往是决定性因素。你是否知道,一个4G模组在信号弱时的瞬时功耗可能超过2A?本文专为刚入门的开发者准备,盘点模组功耗的关键参数与实测注意事项,助你打好基础。 一,模组的低功…

教室照明质量不佳,恐加剧学生近视问题

在当下的教育环境之中&#xff0c;照明质量对学生的视力健康有着直接影响&#xff0c;照明质量对学生的学习效率也有着直接影响&#xff0c;照明质量对学生的视力健康以及学习效率有着深远影响&#xff0c;根据相关调研数据显示&#xff0c;我国5岁以上人群当中近视比例处在35%…

图像的位平面切片综述

一、图像的说明 1.图像的细节 2.图像的纹理 3.图像的噪声 4.图像的轮廓二、关于图像细节 1.图像的细节就是图像中小范围的灰度值的局部变化。 2.图像的细节通常在边缘&#xff0c;线条&#xff0c;点&#xff0c;微小的结构相关 3.比如头发丝&#xff0c;或者树叶的锯齿&#x…