C++11中引入的比较常用的新特性讲解(上)

目录

1、C++11简介

2、统一的列表初始化

2.1、{}初始化

2.2、std::initializer_list

3、变量类型推导

3.1、auto

3.2、decltype

3.3、nullptr

4、范围for循环

5、STL中一些变化

6、右值引用和移动语义

6.1、左值引用和右值引用

6.2、右值引用使用场景和意义

6.3、完美转发


1、C++11简介

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。

不过由于C++03(TC1)主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98 / 03标准。

从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。

相比于C++98 / 03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98 / 03中孕育出的一种新语言。

相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以要作为一个重点去学习。

C++11引入的新特性

关于C++2X最新特性的讨论

查看C++各种库说明的一个好用的网站

C++官网

小故事:
1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,C++国际标准委员会在研究C++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初这个标准叫C++ 07。但是到06年的时候,官方觉得2007年肯定完不成C++ 07,而且官方觉得2008年可能也完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11。C++官网

2、统一的列表初始化

想达到的目的就是:一切都可以用列表{}初始化。

注意:列表初始化跟初始化列表(这是类的构造函数里的东西)不是一个概念。

2.1、{}初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。

C++11扩大了用大括号括起的列表(列表初始化)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用列表初始化时,可添加等号(=),也可不添加。

创建对象时也可以使用列表初始化方式调用构造函数初始化。

栗子:

struct Point
{int _x;int _y;
};
class A
{
public:// explicit A(int x, int y) // explicit关键字,加上之后就不允许隐式类型的转换了A(int x, int y): _x(x), _y(y){}A(int x): _x(x), _y(x){}private:int _x;int _y;
};
void test1()
{// C语言带过来的int array1[] = {1, 2, 3, 4, 5};int array2[5] = {0};Point p = {1, 2}; // 结构体的初始化列表// C++11有的,一切都可用列表初始化int array3[5]{1};int i{1};// 单参数的隐式类型转换A aa1 = 1;A aa2 = {1};A aa3{1};// 多参数的隐式类型转换A aa4 = {1, 2};A aa5{3, 4};const A& aa6 = { 7,7 }; // 不加 const 是不行的,因为这中间有临时变量,临时变量具有常性。 // 注:这句代码在 C++11 之后才支持
}

2.2、std::initializer_list

std::initializer_list的介绍文档

栗子:

void test2()
{// the type of il is an initializer_listauto il = {10, 20, 30};initializer_list<int> il2 = {10, 20, 30};cout << typeid(il).name() << endl;cout << sizeof(il2) << endl; // 8/16// std::initializer_list 虽然是一个容器,但是它本身并没有去新开空间,本质就是两个指针,一个begin指向常量数组的开头,一个end指向常量数组的结尾vector<int> v1;vector<int> v2(10, 1);// 隐式类型转换vector<int> v3 = {1, 2, 3, 4, 5};vector<int> v4{10, 20, 30};// 构造vector<int> v5({10, 20, 30});// 补充:X自定义 = Y类型 --> 想要达成隐式类型转换,则 自定义X 必须支持 Y 为参数类型的构造// 1、pair多参数隐式类型转换// 2、initializer_list<pair>的构造map<string, string> dict = {{"sort", "排序"}, {"insert", "插入"}}; // 在没有支持initializer_list之前,这一句代码可是要分成三句写的,有了initializer_list之后,就方便了很多
}

总结:当容器想用不固定的数据个数初始化时,initializer_list就派上用场了。

注:所有的容器都支持 initializer_list。

3、变量类型推导

c++11提供了多种简化声明的方式,尤其是在使用模板时。

3.1、auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。

C++11中废弃auto原来的用法,将其用于实现自动类型推断。

这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。

栗子:

void test3()
{int i = 0;auto &x = i; // auto可以加引用&++x;		 // x的修改会影响iint &j = i;auto y = j; // 这里的 y 是单纯的 int 还是 int& 呢?// 答:从语法层面上讲 y 就是 int,也可以打开监视窗口,&y,&j,&i 看看,会发现y和j、i的地址不一样,j、i的地址是一样的//     j 虽然是 i 的别名(int&),但 j 的本质也是int。// 所以:这里就是一个很普通的拷贝,实际上这句代码就等价于 auto y = i;++y;pair<string, string> kv = {"sort", "排序"};// auto [x, y] = kv; // 这是C++17支持的一种写法(当前编译器默认支持到C++14)
}

3.2、decltype

关键字decltype将变量的类型声明为表达式指定的类型。

栗子:

template <class T>
class B
{
public:T *New(int n){return new T[n];}
};
auto func2()
{list<int> it;auto ret = it.begin();return ret;
}
auto func1()
{auto ret = func2();return ret;
}
void test4()
{list<int>::iterator it1;cout << typeid(it1).name() << endl; // typeid 是能直接拿到这个变量类型的最原始的名字(字符串)(你所看到的类型名可能是typedef过的)// typeid(it1).name() it2; // typeid 推出的只是一个单纯的字符串,不能用来定义一个新的对象// decltype 可以帮你推断出()内的变量的类型,并且你可以直接使用 decltype 推断出的结果(类型)来定义新的变量。decltype(it1) it2; // it2和it1的类型是一样的cout << typeid(it2).name() << endl;// 光从上面这几句代码,体现不出decltype的作用,因为auto也有这样的功能,而且写起来还更方便auto it3 = it1;cout << typeid(it3).name() << endl;auto ret = func1();// 此时如果你想要用ret的类型去实例化出一个B类型的对象,该怎么办?(假设这里的func不只套了这么几层,套了好多层,那你想要知道 ret 到底是什么类型,就会很麻烦)// 那么 decltype 此时就派上用场了,decltype 推断出的结果(类型)可以用来做模板的传参B<decltype(ret)> bb1;map<string, string> dict = { {"insert","插入"}, {"erase","删除"} };auto it4 = dict.begin();B<decltype(it4)> bb2;B<map<string, string>::iterator> bb3; // 这句代码可读性更好
}

注:decltype 感觉就是跟 auto 配套使用的,用来解决一些auto搞出的问题

总结:auto 和 decltype 还是要慎用,虽然两者都可以帮助你缩短代码量,但是很影响可读性。

3.3、nullptr

注:也是为了解决一些历史遗留的问题而造出的东西。

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能表示指针常量,又能表示整形常量。

所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

4、范围for循环

栗子:

void test5()
{map<string, string> m = {{"sort", "排序"}, {"insert", "插入"}};for (auto &[x, y] : m) // 这里的auto最好加上引用&和const(无需修改就加上const),否则会有深拷贝的问题(如果有大量的string要拷贝的话,会影响程序的效率){cout << x << ":" << y << endl;// x += "1"; // map里的key是不能被修改的y += "1";}
}

5、STL中一些变化

总结:

  1. 增加的4个新容器中,也就 unordered_map、unordered_set 有点用,另外两个(array、forward_list)没什么屁用。
  2. 给所有容器添加了 initializer_list 构造。
  3. 给所有容器添加了 移动赋值和移动构造。
  4. 给所有容器添加了 emplace系列(与右值引用和模板的可变参数有关)。

6、右值引用和移动语义

6.1、左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,之前学习的引用就叫做左值引用。

无论左值引用还是右值引用,都是给对象取别名。

那么什么是左值?什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址,左值可以出现在赋值符号的左边/右边。

而右值则不能出现在赋值符号的左边。

左值引用就是给左值的引用,给左值取别名。

那么什么是右值?什么是右值引用?

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等。

右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。

右值引用就是对右值的引用,给右值取别名。

总之,简单说可以取地址的就是左值,不能取地址的就是右值。

注:不能说可以修改的就是左值,不能修改的就是右值。

栗子:

const int val = 7; // 这里的 val 还是左值void test6()
{// 左值:可以取地址的int a = 10;int b = a;const int c = 10;int *p = &a;vector<int> v(10, 1);(void)v[1]; // 强转成 void 取消警告cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &(*p) << endl;cout << &(v[1]) << endl;// 右值:不可以取地址的// 10、string("111")、to_string(123)、x+y// cout << &10 << endl; // 字面常量// cout << &string("111") << endl; // 匿名对象// cout << &to_string(123) << endl; // 该函数的返回值返回的是一个临时拷贝、临时对象int x = 1, y = 2;// cout << &(x + y) << endl; // 表达式返回值// 几个右值引用的栗子// 为了方便理解,补充两个概念:纯右值(内置类型) 将亡值(自定义类型)int &&rref1 = (x + y);			 // 纯右值(内置类型)string &&rref2 = string("111");	 // 将亡值(自定义类型) // 生命周期就这一行string &&rref3 = to_string(123); // 将亡值(自定义类型) // 生命周期就这一行int &&rref4 = 10;				 // 纯右值(内置类型)// 左值引用能否给右值取别名?// 答:不可以,但是 const 左值引用可以const string &ref1 = string("111");const int &ref2 = 10;// 右值引用能否给左值取别名?// 答:不可以,但是可以给 move 以后的左值取别名string s1("222");// string&& rref5 = s1;string &&rref6 = move(s1);// 补充:当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。//		 C++11中,std::move()函数位于 头文件中,该函数名字具有迷惑性,它并不搬移任何东西,//		 唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。// 下面的s2是左值还是右值呢?string &&s2 = string("111");// 要验证s2是左值还是右值实际上很简单,只需要看一下能不能取的出s2的地址就好了。cout << &s2 << endl;// 总结:右值引用本身的属性是左值!!!//       需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址。// 下面的移动构造和移动赋值也说明了右值引用的属性是左值。// 因为只有右值引用本身被处理成了左值,才能实现移动构造和移动赋值,才能转移资源。-- 因为中间要 swap// 通过反汇编也是能看到右值是有地址的,只是语法上规定不能取。(没有地址的话,右值存在哪里呢?右值也得有个地方给它存吧)// 右值引用本身是左值的意义是:为了 移动构造和移动赋值 中要 转移资源 的语法逻辑能够逻辑自洽。// 如果右值引用的属性是右值,那么移动构造和移动赋值中要转移资源的语法逻辑就是矛盾的了。// 右值是不能被改变的。(可以理解为右值带有const属性)// 补充:即使没有右值引用,实际上也是有办法能够改变右值的// ex:string& s = (string&)string("kk"); // 强转可以让普通的左值引用直接引用右值// 因为不管是什么值,终归要有空间去存储它,有空间,不就能改了嘛。
}

6.2、右值引用使用场景和意义

前面我们可以看到左值引用既可以引用左值和又可以引用右值。

那为什么C++11还要提出右值引用呢?是不是化蛇添足呢?

下面我们来看看左值引用的短板,右值引用是如何补齐这个短板的!

首先,引用的意义是什么?

答:引用的意义就是为了减少拷贝,提高效率。

比如:

左值引用解决的场景

void function1(const string &s); // 减少传参时的拷贝的消耗string &function2();             // 传引用返回,减少拷贝的消耗

但是左值引用没有彻底解决返回值的拷贝消耗问题,因为不是什么情况下都能传引用返回的。

比如当返回值是function2中的局部对象,就不能用引用返回。

举个栗子:

namespace kk
{class string{public:typedef char *iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char *str = ""): _size(strlen(str)), _capacity(_size){cout << "string(char* str) -- 构造" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(kk::string &s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 拷贝构造(左值引用)string(const kk::string &s) // 左值的拷贝构造会去调用这个: _str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}// 移动构造(右值引用) -- 移动将亡值的资源string(kk::string &&s) // 右值的移动(拷贝)构造会去调用这个: _str(nullptr){cout << "string(string&& s) -- 移动构造" << endl;swap(s);}// 赋值重载kk::string &operator=(const kk::string &s){cout << "string& operator=(string s) -- 深拷贝" << endl;char *tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}// 移动赋值kk::string &operator=(kk::string &&s){cout << "operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char &operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char *tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}// string operator+=(char ch)kk::string &operator+=(char ch){push_back(ch);return *this;}const char *c_str() const{return _str;}private:char *_str = nullptr;size_t _size = 0;size_t _capacity = 0; // 不包含最后做标识的\0};kk::string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}kk::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str; // 虽然这里的str是左值,但是你加不加move,都是可以的,因为编译器已经帮你处理好了(编译器会强行认为这是右值),它都会去调用移动构造/移动赋值。// 注意:右值引用/这里的移动构造 并不会延长对象的生命周期,str的生命周期到了,该销毁还是销毁,只是发生了资源转移。(严格来说,是延长了资源的生命周期)}
}

总结:右值引用彻底拯救了传值返回所引发的效率低下的问题。一般的传值返回需要进行 一次拷贝构造 + 一次赋值/拷贝构造 -- 拷贝构造和普通的赋值的成本很大。有了右值引用之后,传值返回变成了 一次移动构造 + 一次移动赋值/移动构造 -- 移动构造和移动赋值只是对资源的转移,成本很低。

举个栗子:

vector<vector<int>> func(int rows); // 该函数 在C++11之前(右值引用出来之前) 不能这样写,因为效率十分的低下!!!void func(int rows, vector<vector<int>>& vv); // 该函数 在C++11之前(右值引用出来之前) 只能这样写,才能保证效率。

6.3、完美转发

直接上代码:

void Fun(int &x) { cout << "左值引用" << endl; }
void Fun(const int &x) { cout << "const 左值引用" << endl; }
void Fun(int &&x) { cout << "右值引用" << endl; }
void Fun(const int &&x) { cout << "const 右值引用" << endl; }
// std::forward 完美转发在传参的过程中保留对象原生类型属性
// std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
// 在函数模板里面,这里可以称之为万能引用(引用折叠)
template <typename T>
void PerfectForward(T &&t) // 注意:模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。//		这里的 T&& 这一坨都是模板,而不只是那个T。//		这里的t的属性根据你传过来的参数推导。//		你传的是左值,t就是左值,你传的右值,t就是右值。//		你传的 const 左值,t就是 const 左值。//		你传的 const 右值,t就是 const 右值。
{Fun(t);					 // 如果仅仅只是这样写传参的话,最后走的全是 左值引用 和 const 左值引用。-- 因为右值引用的属性还是左值Fun(std::forward<T>(t)); // 要这样写,才能保证t的属性不变,不会发生从右值退化成左值(右值引用)的情况。-- 注意不能用 move,因为这里是模板,你不知道传过来的值本身是左值还是右值引用(右值退化成左值)?用了 move,就变成了全部都是调用 右值引用 和 const右值引用Fun((T &&)t);			 // 这样写的效果跟完美转发一样。 // 但要注意,这不是官方的写法,不太清楚这种写法有没有什么缺陷。
}
void test7()
{PerfectForward(10); // 右值int a;PerfectForward(a);			  // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);			  // const 左值PerfectForward(std::move(b)); // const 右值
}

总结:完美转发的意义就是在一个既能接收左值又能接收右值的函数模板中,当需要保持一个右值引用的属性保持不变,不想其退化成左值的时候,就用完美转发。

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

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

相关文章

hugo+github pages 部署实验室网站

&#xff01;&#xff01;太爽了&#xff0c;看了很久教程&#xff0c;自己试了好久&#xff0c;终于搞懂怎么把hugo和public单独进行部署了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 目的是什么&#xff1f;目的当然是为了修改这天杀的hugo的模板。现在…

两头文件互引问题解决(前置声明)

问题&#xff1a; 想必正在基础阶段学习的朋友们都遇见过以下问题吧 widget.h otherwidget.h 如上问题是&#xff0c;我在widget.h中引用了otherwidget.h ,在ohterwidget中又引用了widget.h&#xff0c;两个头文件互相引用产生了报错 解决办法&#xff1a; 那么我们该如何解…

Python 异常处理完全指南

目录 一、异常处理基础1. 基本语法结构 二、常见异常类型1. 内置异常层次2. 常见异常示例 三、多重异常处理1. 合并捕获2. 分层处理 四、finally与else子句1. finally 应用场景2. else 使用技巧 五、自定义异常1. 创建异常类2. 异常继承体系 六、异常链与上下文1. raise from 语…

【Qt】Ubuntu22.04使用命令安装Qt5和Qt6

1、安装Qt5 注意:Ubuntu22.04已经没有 qt5-default ,因此不能一键安装啦 1)安装核心组件 sudo apt install qtbase5-dev qtchooser qt5-qmake qtcreator2)安装QtCreator sudo apt install qtcreator3)安装工具包、Qt Quick 开发的核心库(qtdeclarative5-dev) sudo a…

手撸一个 deepseek 数据库对话,打造一个企业智能通讯录(ollama + deepseek + langchain)

前言 由于 deepseek 等大语言模型数据时效性问题&#xff0c;无法跟上现实世界信息的动态变化&#xff0c;企业内部信息更是无法理解&#xff0c;为了将 deepseek 应用到企业内部&#xff0c;之前有写过通过联网搜索、上传文件、搭建知识等检索增强生成&#xff08;RAG&#xf…

线段树SegmentTree

线段树当中的几个重要操作 1.PushUp 上推操作&#xff1a;由子节点算父节点的信息 p u s h u p push up pushup 操作的目的是为了维护父子节点之间的逻辑关系。当我们递归建树时&#xff0c;对于每一个节点我们都需要遍历一遍&#xff0c;并且电脑中的递归实际意义是先向底层…

SSH免密登录服务器方法

Window免密连接Linux系统 生成公匙 ssh-keygen -t rsa一路回车生成公钥 复制公匙&#xff0c;使用记事本打开复制全部内容 notepad C:\Users\DELL\.ssh\id_rsa.pub内容如"ssh-rsa AAAAB3NzaC1yc2EAAAA…" 远程登录服务器将内容写入~/.ssh/authorized_keys echo …

Go 1.24 新特性解析:泛型类型别名、弱指针与终结器改进

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…

MySQL 表 t1 建立联合索引 (a, b, c),在 where a < ? and b > ? and c < ? 中哪些索引生效

文章目录 联合索引 abc 均范围扫描时的索引生效情况无回表 表数据量非常少无回表 表数据量多有回表总结 联合索引 abc 均范围扫描时的索引生效情况 场景&#xff1a;表 t1 建立联合索引 (a, b, c)&#xff0c;在 where a < ? and b > ? and c < ? 中哪些索引生效…

海外营收占比近4成,泡泡玛特全球化战略迎收获期

3月26日&#xff0c;泡泡玛特国际集团发布2024全年财报。财报显示&#xff0c;2024年泡泡玛特实现营收130.4亿元&#xff08;人民币&#xff0c;下同&#xff09;&#xff0c;同比增长106.9%&#xff0c;经调整净利润34.0亿元&#xff0c;同比增长185.9%。中国内地营收79.7亿元…

ctf-web: 不统一的解析 + sql注入要求输入与输出相等 -- tpctf supersqli

# 从 django.shortcuts 模块导入 render 函数&#xff0c;用于渲染模板 from django.shortcuts import render # 从 django.db 模块导入 connection 对象&#xff0c;用于数据库连接 from django.db import connection# 此模块用于创建视图函数 # 从 django.http 模块导入 Http…

LLM推理加速框架有哪些

LLM推理加速框架有哪些 目录 LLM推理加速框架有哪些1. TensorRT简介简单使用示例2. Triton Inference Server简介简单使用示例3. SGLang简介简单使用示例4. vLLM简介简单使用示例1. TensorRT 简介 TensorRT 是 NVIDIA 推出的一个用于高性能深度学习推理的 SDK。它能够对训练好…

【深度学习与实战】2.1、线性回归模型与梯度下降法先导案例--最小二乘法(向量形式求解)

为了求解损失函数 对 的导数&#xff0c;并利用最小二乘法向量形式求解 的值&#xff0c;我们按照以下步骤进行&#xff1a; ‌1. 损失函数的含义‌ 这是‌线性回归‌的平方误差损失函数&#xff0c;目标是最小化预测值 与真实值 之间的差距。 ‌定义损失函数‌&#xf…

S7-1200对V90 PN进行位置控制的三种方法

S7-1200系列PLC通过PROFINET与V90 PN伺服驱动器搭配进行位置控制,实现的方法主要有以下三种: ? 方法一、在PLC中组态位置轴工艺对象,V90使用标准报文3,通过MC_Power、MC_MoveAbsolute等PLC Open标准程序块进行控制, 这种控制方式属于中央控制方式(位置控制在PLC中计算,驱…

爱普生FC-135晶振5G手机的极端温度性能守护者

在5G时代&#xff0c;智能手机不仅需要高速率与低延迟&#xff0c;更需在严寒、酷暑、振动等复杂环境中保持稳定运行。作为 5G 手机的核心时钟源&#xff0c;爱普生32.768kHz晶振FC-135凭借其宽温适应性、高精度稳定性与微型化设计&#xff0c;成为5G手机核心时钟源的理想选择&…

ROS--IMU数据包

IMU惯性测量单元 一&#xff1a;IMU二&#xff1a;ROS中三&#xff1a;IMU数据包三&#xff1a;总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一&#xff1a;IMU IMU&#xff08;Inertial Measurement Unit&#xff0c;惯性测量单元&#xff09…

数据文件误删除,OceanBase中如何重建受影响的节点

当不慎误删数据文件且当前没有现成的可替换节点时&#xff0c;在OceanBase中&#xff0c;不必急于采取极端措施&#xff0c;可以考虑运用 server_permanent_offline_time 参数&#xff0c;来重建受影响的节点。 原理&#xff1a; server_permanent_offline_time 是 OceanBase数…

Python:匹配多个字符,如何匹配开头

匹配字符0次或无数次(*)&#xff1a; import re resre.match([A-Z][a-z]*,Lihailu) print(res.group())#提取数据 输出结果可以全部输出 匹配字符至少一次()&#xff1a; import re resre.match([A-Za-z]python,apython) print(res.group())#提取数据(后边只写python会…

Unity-RectTransform设置UI width

不知道有没人需要这样的代码&#xff0c;就是.sizeDelta //不确定是不是英文翻译的原因&#xff0c;基本很难理解&#xff0c;sizeDeltaSize&#xff0c;//未必完全正确&#xff0c;但这么写好像总没错过 //image 在一个UnityEngine.UI.Image 的数组内foreach (var image in l…

java学习——函数式编程(1)

函数式编程 Java 的函数式编程是一种以函数为核心构建逻辑的编程范式,强调不可变性、声明式代码和无副作用的操作。它通过Lambda表达式、函数式接口(如Function、Predicate、Consumer等)和Stream API等特性实现,将计算过程抽象为函数的组合与转换,而非传统的命令式步骤。…