C++11右值与列表初始化

1.列表初始化

C++98传统的{}

C++98中一般数组和结构体可以用{}进行初始化。

struct Point
{int _x;int _y;
};
int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0;
}

C++11中的{}

C++11以后统一初始化方式,想要实现一切对象皆可用{}初始化,{}初始化也叫做列表初始化

内置类型支持,自定义类型也支持,自定义类型本质是类型转换,中间会产生临时对象,最后优化了以后就只有构造

{}初始化过程可以省略=

代码示例

#include<iostream>
#include<vector>
using namespace std;struct Point
{int _x;int _y;
};class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){cout << "Date(int year,int month,int day)" << endl;}Date(const Date& d):_year(d._year),_month(d._month),_day(d._day){cout << "Date(const Date& d)" << endl;}private:int _year;int _month;int _day;
};int main()
{//C++11支持//内置类型支持int x1 = { 2 };//自定义类型支持//这里本质是用{2025,1,1}构造一个Date临时对象//临时对象再去拷贝构造d1,编译器优化后会合二为一变成{2025,1,1}直接构造Date d1 = { 2025,1,1 };//这里d2引用的是{2024,7,25}构造的临时对象const Date& d2 = { 2024,7,25 };//C++98支持单参数类型转换,也可以不用{}Date d3 = { 2025 };Date d4 = 2025;//可以省略掉=Point p1{ 1,2 };int x2{ 2 };Date d6{ 2024,7,25 };const Date& d7{ 2024,7,25 };//只有{}初始化才能省略=//不支持 Date d8 2025vector<Date> v;v.push_back(Date(2025, 1, 1));v.push_back(d1);//对于匿名对象和匿名对象传参,这里{}相对好v.push_back({ 2025,1,1 });return 0;}

C++11中std::initializer_list

上面初始化很方便,当对于容器的初始化还是不方便,像vector对象,想用N个值去构造初始化,就得实现很多个构造函数才能支持

vector<int> v1 = {1,2,3};vector<int> v2 = {1,2,3,4,5};
C++11库中提出了一个std::initializer_list的类, auto il = { 10, 20, 30 }; // the type of il is an initializer_list,这个类本质是底层开一个数组,将数据拷贝过来,std::initializer_list内部有俩个指针分别指向数组的开始和结束。
容器支持一个std::initializer_list的构造函数,也就支持任意多个值构成的{x1,x2,x3....}进行初始化。STL中的容器支持任意多个值构成的{x1,x2,x3...}进行初始化,就是通过std::initializer_list的构造函数支持的。
// STL 中的容器都增加了⼀个 initializer_list 的构造
vector (initializer_list<value_type> il, const allocator_type& alloc =
allocator_type ());
list (initializer_list<value_type> il, const allocator_type& alloc =
allocator_type ());
map (initializer_list<value_type> il, const key_compare& comp =
key_compare (), const allocator_type& alloc = allocator_type ());
// ...
template < class T >
class vector {
public :
        typedef T* iterator;
        vector (initializer_list<T> l)
        {
        for ( auto e : l)
                push_back (e)
        }
private :
        iterator _start = nullptr ;
        iterator _finish = nullptr ;
        iterator _endofstorage = nullptr ;
};
// 另外,容器的赋值也⽀持 initializer_list 的版本
vector& operator = (initializer_list<value_type> il);
map& operator = (initializer_list<value_type> il);
# include <vector>
# include <string>
# include <map>
using namespace std;
int main ()
{
        std::initializer_list< int > mylist;
        mylist = { 10 , 20 , 30 };
        cout << sizeof (mylist) << endl;
        // 这⾥ begin end 返回的值 initializer_list 对象中存的两个指针
        // 这两个指针的值跟 i 的地址跟接近,说明数组存在栈上
        int i = 0 ;
        cout << mylist. begin () << endl;
        cout << mylist. end () << endl;
        cout << &i << endl;
        // {}列表中可以有任意多个值
        // 这两个写法语义上还是有差别的,第⼀个 v1 是直接构造,
        // 第⼆个 v2 是构造临时对象 + 临时对象拷⻉ v2+ 优化为直接构造
        vector< int > v1 ({ 1 , 2 , 3 , 4 , 5 });
        vector< int > v2 = { 1 , 2 , 3 , 4 , 5 };
        const vector< int >& v3 = { 1 , 2 , 3 , 4 , 5 };
        // 这⾥是 pair 对象的 {} 初始化和 map initializer_list 构造结合到⼀起⽤了
        map<string, string> dict = { { "sort" , " 排序 " }, { "string" , " 字符串 " }};
        / / initializer_list版本的赋值⽀持
        v1 = { 10 , 20 , 30 , 40 , 50 };
        return 0 ;
}

 右值引用和移动语义

C++98就有引用,在C++11新增了右值引用语法特性,C++11之后之前的学的就叫左值引用。无论左值还是右值引用,都是给对象取别名。

左值和右值

左值是一个表示数据的表达式,一般是有持久状态,存储在内存中,可以获取1它的地址,左值可以出现在赋值符号的左边,也可以出现在右边。定义时const修饰符后的左值,不能给它赋值,可以取地址。

右值也是一个表示数据的表达式,要么是字面值常量,要么是表达式求值过程中创建的临时对象等,右值可以出现在赋值符号的右边,但是不能出现在左边,右值不能取地址。

左右值核心区别在于能不能取地址。

代码示例

#include<iostream>
using namespace std;int main()
{//以下p,b,c,*p,s,s[0]就是常见的左值int* p = new int(0);int b = 1;const int c = b;*p = 10;string s("111111");s[0] = 'x';cout << &s << endl;cout << (void*)&s[0] << endl;//右值不能取地址double x = 1.1, y = 2.2;10;x + y;fmin(x, y);//参数为浮点数,返回较小的浮点数string("111111");return 0;
}

补充:用(void*)强制转换是为了打印出地址,没有就会打印出值 

左值引用和右值引用

Type% r1=x;Type&& rr1=y; 第一个语句就是左值引用,左值引用就是给左值取别名,右值引用就是给右值取别名。

左值引用不能直接引用右值,但是const左值可以引用右值。

右值引用不能直接引用左值,但是右值可以引用move(左值)

template <class T> typename remove_reference<T>::type&& move (T&& arg);

move是库里面的一个函数模板,本质内部是进行强制类型转换。

需要注意变量表达式都是左值属性,也就说一个右值被右值引用绑定后,右值引用变量表达式的属性是左值。

代码示例

#include<iostream>using namespace std;int main()
{int* p = new int(0);int b = 1;const int c = b;*p = 10;string s("11111");s[0] = 'x';double x = 1.1, y = 2.2;//左值引用给左值取别名int& r1 = b;int*& r2 = p;int& r3 = *p;string& r4 = s;char& r5 = s[0];//右值引用给右值取别名int&& rr1 = 10;double&& rr2 = x + y;//表达式返回值是临时变量double&& rr3 = fmin(x, y);//返回值临时变量string&& rr4 = string("11111");//匿名对象,活不过下一行//左值引用不能直接引用右值,但是const左值可以引用右值const int& rx1 = 10;const double& rx2 = x + y;const double& rx3 = fmin(x, y);const string& rx4 = string("11111");//右值引用不能直接用于左值,但是可以引用move(左值)int&& rrx1 = move(b);int*&& rrx2 = move(p);int&& rrx3 = move(*p);string&& rrx4 = move(s);string&& rrx5 = (string&&)s;//跟move(s)一样//int&& r1=10,此时rr1的属性是左值,所以不能再被右值引用绑定,除非moveint& r6 = rr1;//因为rr1的属性是左值所以左值引用不用加const就可以引用rr1//int&& rrx6=rr1 这里是不行的,右值不能直接引用左值int&& rrx6 = move(rr1);return 0;
}

引用延长生命周期

右值引用可以为临时变量延长生命周期,const的左值引用也可以延长临时对象生命周期,但是这些对象无法修改。

代码示例

int main()
{std::string s1 = "Test";const std::string& r2 = s1 + s1;//const的左值引用延长了生命周期,但是不能被修改std::string&& r3 = s1 + s1;//右值引用延长生命周期r3 += "Test";//能通过非constreturn 0;
}

左值和右值的参数匹配

在C++98中,实现一个const左值引用作为参数的函数,则实参传递左值和右值都可以匹配。

C++11后,分别重载左值引用,const左值引用,右值引用作为形参的f函数,可以试验前面的结论

代码示例

#include<iostream>using namespace std;void f(int& x)
{cout << "左值引用重载\n" << endl;
}void f(const int& x)
{cout << "const左值引用重载\n" << endl;
}void f(int&& x)
{cout << "右值引用重载\n" << endl;
}int main()
{int  i = 1;const int ci = 2;f(i);//f(int&)f(ci);//f(const int&)f(3);//f(int&&),没有f(int&&),就会走f(const int&)f(move(i));//f(int&&)int&& x = 1;f(x);//右值引用在用于表达式时是左值f(move(x));//f(int&&)return 0;
}

在 C++ 中,右值引用可以延长生命周期的原因在于它们专门设计用来处理临时对象(右值),而左值引用则是用来引用可以持续存在于内存中的对象(左值)。以下是为什么右值引用可以延长生命周期,而左值引用不可以的详细解释:

### 右值引用延长生命周期的原因:

1. **临时对象的生命周期**:
   - 临时对象(右值)通常在表达式求值结束后立即销毁。右值引用通过绑定到这些临时对象,可以延长它们的生命周期,使得它们可以被多次使用,直到右值引用本身被销毁或再次赋值。

2. **移动语义**:
   - 右值引用是实现移动语义的关键。移动语义允许资源(如动态内存、文件句柄等)从一个地方“移动”到另一个地方,而不是复制。这种转移是通过右值引用实现的,它允许临时对象的资源被转移给另一个对象,从而延长了资源的生命周期。

3. **优化和性能**:
   - 使用右值引用可以避免不必要的复制,提高程序性能。例如,在函数返回临时对象时,如果使用右值引用,可以直接将资源转移给调用者,而不是创建一个临时对象的副本。

### 左值引用不延长生命周期的原因:

1. **持久性**:
   - 左值引用是设计用来引用具有持久存储的对象。它们指向对象的身份,而不是值。左值引用的目的是提供对对象的直接访问,而不是延长其生命周期。

2. **别名问题**:
   - 如果左值引用可以延长临时对象的生命周期,那么它可能会引入别名问题(aliasing)。例如,如果两个左值引用指向同一个对象,对其中一个引用的修改可能会影响另一个引用,这在很多情况下是不希望发生的。

3. **语义清晰**:
   - 左值引用和右值引用有不同的语义。左值引用意味着对对象的持久引用,而右值引用意味着对临时对象的引用。这种区分有助于编译器优化和程序员理解代码。

4. **避免悬挂引用**:
   - 如果左值引用可以延长临时对象的生命周期,那么在对象被销毁后,引用仍然存在,这将导致悬挂引用(dangling reference),即引用指向一个已经不再有效的内存区域。

总结来说,右值引用可以延长生命周期是因为它们专门设计用来处理临时对象,并且是实现移动语义的关键。而左值引用不延长生命周期是因为它们设计用来引用持久存储的对象,并且延长临时对象的生命周期可能会导致别名问题和悬挂引用等问题。这种设计使得左值引用和右值引用在语义上保持清晰,有助于编译器优化和代码理解。
 

 右值引用和移动语义的使用场景

左值引用主要使用场景

左值引用主要使用场景是在函数中左值引用传参和左值引用返回值时减少拷贝,同时还可以修改实参和修改返回对象的值。左值引用已经解决大多数场景的拷贝效率问题,但是有些场景不能使用传左值引用返回,如addStrings和generate函数。而C++11以后的右值引用也不可以解决,这里本质是返回对象是一个局部对象,函数结束这个对象就析构销毁,也就是给了一个野指针。

代码示例


class Solution {
public:// 传值返回需要拷⻉string addStrings(string num1, string num2) {string str;int end1 = num1.size() - 1, end2 = num2.size() - 1;// 进位int next = 0;while (end1 >= 0 || end2 >= 0){int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;int ret = val1 + val2 + next;next = ret / 10;ret = ret % 10;str += ('0' + ret);}if (next == 1)str += '1';reverse(str.begin(), str.end());return str;}
};
class Solution {
public:// 这⾥的传值返回拷⻉代价就太⼤了vector<vector<int>> generate(int numRows) {vector<vector<int>> vv(numRows);for (int i = 0; i < numRows; ++i){vv[i].resize(i + 1, 1);}for (int i = 2; i < numRows; ++i){for (int j = 1; j < i; ++j){vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];}}return vv;}
};

移动构造和移动赋值

移动构造函数是一种构造函数,类似于拷贝构造,移动构造函数要求第一个参数是该类类型的引用,但是不同的是要求这个参数是右值引用,如果还有其它参数,额外参数必须有缺省值。

移动赋值是一个赋值运算符的重载,它跟拷贝赋值构成函数重载,类似拷贝函数,移动赋值函数要求第一个参数是该类类型的引用,但是不同的是要求这个参数是右值引用。

对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类,移动构造和移动赋值才有意义,因为移动构造和移动赋值的第一个参数都是右值引用类型,本质是要抢夺引用的右值对象资源,而不是像拷贝构造和拷贝赋值去拷贝资源。

代码示例

在参数为string&& s的移动构造里,是通过swap函数交换俩个指向的内容,效率是比拷贝高的,同样移动赋值也是交换指向的内容。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
#include<string.h>
#include<algorithm>
using namespace std;
namespace bit
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{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);}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}string(const string& s):_str(nullptr){cout << "string(const string& s) -- 拷贝构造" << endl;reserve(s._capacity);for (auto ch : s){push_back(ch);}}// 移动构造string(string&& s){cout << "string(string&& s) -- 移动构造" << endl;swap(s);}string& operator=(const string& s){cout << "string& operator=(const string& s) -- 拷⻉赋值" <<endl;if (this != &s){_str[0] = '\0';_size = 0;reserve(s._capacity);for (auto ch : s){push_back(ch);}}return *this;}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;}~string(){cout << "~string() -- 析构" << endl;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];if (_str){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){push_back(ch);return *this;}const char* c_str() const{return _str;}size_t size() const{return _size;}
private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;
};
}
int main()
{bit::string s1("xxxxx");// 拷⻉构造bit::string s2 = s1;// 构造+移动构造,优化后直接构造bit::string s3 = bit::string("yyyyy");// 移动构造bit::string s4 = move(s1);cout << "******************************" << endl;return 0;
}

右值引用和移动语义解决传值返回问题

场景一是匿名对象,参数为匿名对象就会走移动拷贝,直接交换指向内容,场景二是先创建了一个对象,然后在用移动赋值。

namespace bit
{string addStrings(string num1, string num2){string str;int end1 = num1.size() - 1, end2 = num2.size() - 1;int next = 0;while (end1 >= 0 || end2 >= 0){int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;int ret = val1 + val2 + next;next = ret / 10;ret = ret % 10;str += ('0' + ret);}if (next == 1)str += '1';reverse(str.begin(), str.end());cout << "******************************" << endl;return str;}
}
// 场景1
int main()
{bit::string ret = bit::addStrings("11111", "2222");cout << ret.c_str() << endl;return 0;
}
// 场景2
int main()
{bit::string ret;ret = bit::addStrings("11111", "2222");cout << ret.c_str() << endl;return 0;
}

补充

临时变量和匿名对象区别在一个是自己创建的,一个是编译器生成的

对于一个函数内部变量的生命周期,如果给这个变量改变生命周期是不行的,因为函数结束后会销毁栈区,作为这个栈区的内部变量也会销毁,被跟着带走了

延长生命周期前提是存储空间都在,引用对象的存储空间不在就无法延长生命周期

把左值move后,就给这个左值赋予被抢夺资源的标签

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

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

相关文章

设计模式 创建型 建造者模式(Builder Pattern)与 常见技术框架应用 解析

单例模式&#xff08;Singleton Pattern&#xff09;&#xff0c;又称生成器模式&#xff0c;是一种对象构建模式。它主要用于构建复杂对象&#xff0c;通过将复杂对象的构建过程与其表示分离&#xff0c;使得同样的构建过程可以创建出具有不同表示的对象。该模式的核心思想是将…

什么是Redis哨兵机制?

大家好&#xff0c;我是锋哥。今天分享关于【什么是Redis哨兵机制&#xff1f;】面试题。希望对大家有帮助&#xff1b; 什么是Redis哨兵机制&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis 哨兵&#xff08;Sentinel&#xff09;机制是 Redis 提…

全国计算机设计大赛大数据主题赛(和鲸赛道)经验分享

全国计算机设计大赛大数据主题赛&#xff08;和鲸赛道&#xff09;经验分享 这是“和鲸杯”辽宁省普通高等学校本科大学生计算机设计竞赛启动会汇报—大数据主题赛的文档总结。想要参加2025年此比赛的可以借鉴。 一、关于我 人工智能专业 计赛相关奖项&#xff1a; 2022年计…

STM32 + 移远EC800 4G通信模块数传

一、4G模块简述 EC800M-CN 是移远通信&#xff08;Quectel&#xff09;推出的一款高性能、超小尺寸的 LTE Cat 1 无线通信模块&#xff0c;专为 M2M&#xff08;机器对机器&#xff09;和 IoT&#xff08;物联网&#xff09;应用设计。它具有以下主要特点&#xff1a; 通信速率…

标准库以及HAL库——按键控制LED灯代码

按键控制LED本质还是控制GPIO,和点亮一个LED灯没什么区别 点亮一个LED灯&#xff1a;是直接控制输出引脚&#xff0c;GPIO初始化推挽输出即可 按键控制LED&#xff1a;是按键输入信号从而控制输出引脚&#xff0c;GPIO初始化推挽输出一个引脚以外还得加一个GPIO上拉输入 但是…

springboot525基于MVC框架自习室管理和预约系统设计与实现(论文+源码)_kaic

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装自习室管理和预约系统软件来发挥其高效地信息处理的作用&am…

Unity Canvas中显示粒子特效

首先在场景中新建一个粒子特效 修改一下参数 1.改变粒子特效的渲染层级,层级修改为UI层,由UI相机渲染 使用粒子特效的Sorting Layer ID和Order In Layer,Sorting Layer ID设置为UI(如果没有UI层则新建就好了),对UI进行排序 对于要显示在前的UI组件添加Canvas组件,设置O…

Linux下部署Redis集群 - 一主二从三哨兵模式

三台服务器redis一主二从三哨兵模式搭建 最近使用到了redis集群部署&#xff0c;使用一主二从三哨兵集群部署redis&#xff0c;将自己部署的过程中的使用心得分享给大家&#xff0c;希望大家以后部署的过程减少一些坑。 服务器准备 3台服务器 &#xff0c;确定主redis和从red…

服务器端请求伪造之基本介绍

一.服务器端请求伪造漏洞基础 1.客户端请求 客户端请求指的是由客户端设备&#xff08;如个人计算机、智能手机、平板电脑等&#xff09;或软件&#xff08;浏览器、各种APP&#xff09;发出的请求&#xff0c;以获取指定的网页、图片、视频或其他资源。比如当用户在浏览器中输…

akamai3.0反爬教程逆向分析9个视频汇总

目录 一、akamai2.0文章二、akamai3.0每月疑似改版点二、9个视频汇总如下 一、akamai2.0文章 文章1cookie反爬之akamai_2.0-上文章2cookie反爬之akamai_2.0-上文章3cookie反爬之akamai_2.0-上文章中akamai2.0对应调试html与js文件 二、akamai3.0每月疑似改版点 详细文字与2.…

2024年12月 Scratch 图形化(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch图形化等级考试(1~4级)全部真题・点这里 一、单选题(共25题,共50分) 第 1 题 小猫初始位置和方向如下图所示,下面哪个选项能让小猫吃到老鼠?( ) A. B. C.

【74LS160+74LS273DW锁存器8位的使用频率计】2022-7-12

缘由 想知道这个数字频率计仿真哪里出现错误了&#xff0c;一直无法运行哎&#xff0c;如何解决&#xff1f;-运维-CSDN问答

系统思考—信任

《基业长青》作者指出&#xff1a;“在人生的重要十字路口&#xff0c;选择信任是一场赌注。信任带来的好处可能巨大&#xff0c;而失去信任的代价却相对有限。但如果选择不信任&#xff0c;最优秀的人才可能因失望而离开。” 在企业管理中&#xff0c;信任不仅是人际关系的纽…

推理加速:投机采样经典方法

一 SpecInfer 基于模型 SpecInfer&#xff08;[2305.09781] SpecInfer: Accelerating Generative Large Language Model Serving with Tree-based Speculative Inference and Verification&#xff09; SpecInfer 投机采样利用多个小型模型&#xff08;SSM&#xff09;快速生…

深入理解Java中的Set集合:特性、用法与常见操作指南

一、HashSet集合 1.HashSet集合的特点 2.HashSet常用方法 ①&#xff1a;add(Object o)&#xff1a;向Set集合中添加元素&#xff0c;不允许添加重复数据。 ②&#xff1a;size()&#xff1a;返回Set集合中的元素个数 ③.remove(Object o)&#xff1a; 删除Set集合中的obj对…

黑马Java面试教程_P10_设计模式

系列博客目录 文章目录 系列博客目录前言1. 工厂方法模式1.1 概述1.2 简单工厂模式1.2.1 结构1.2.2 实现1.2.3 优缺点 1.3 工厂方法模式1.3.1 概念1.3.2 结构1.3.3 实现1.3.4 优缺点 1.4 抽象工厂模式1.4.1 概念1.4.2 结构1.4.3 实现1.4.4 优缺点1.4.5 使用场景 总结&#xff0…

开源架构的容器化部署优化版

上三篇文章推荐&#xff1a; 开源架构的微服务架构实践优化版&#xff08;New&#xff09; 开源架构中的数据库选择优化版&#xff08;New&#xff09; 开源架构学习指南&#xff1a;文档与资源的智慧锦囊&#xff08;New&#xff09; 我管理的社区推荐&#xff1a;【青云交社区…

SpringCloudAlibaba实战入门之Sentinel服务降级和服务熔断(十五)

一、Sentinel概述 1、Sentinel是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 一句话概括:sentinel即Hystrix的替代品,官网: https://sentinelguard.io/zh…

【每日学点鸿蒙知识】导入cardEmulation、自定义装饰器、CallState状态码顺序、kv配置、签名文件配置

1、HarmonyOS 无法导入cardEmulation&#xff1f; 在工程entry mudule里的index.ets文件里导入cardEmulation失败 可以按照下面方式添加SystemCapability&#xff1b;在src/main/syscap.json(此文件需要手动创建&#xff09;中添加如下内容 {"devices": {"gen…

Datawhale AI冬令营(第二期)动手学AI Agent--Task3:学Agent工作流搭建,创作进阶Agent

目录 一、工作流&#xff1a;制作复杂Agent的福音&#xff01; 二、支付宝百宝箱中工作流介绍 三、设计工作流 3.1 准备功能模块 3.2组合工作流 3.3 模块测试需要注意什么 3.4迭代优化 四、高中学习小助手工作流设计 4.1 选题调研 4.2 功能模块设计 4.3 组合完整工作…