【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)

🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:C++

目录

前言

一、列表初始化

1. 大括号初始化

2. initializer_list

二、右值引用和移动语义

1. 左值和右值

2. 左值引用和右值引用

引用延长生命周期

左值引用和右值引用的参数匹配原则

右值引用的适用场景

引用折叠与万能引用

完美转发

三、类的新默认成员函数

1. 移动构造函数

2. 移动赋值函数

四、lambda表达式

1. 捕捉列表

显式捕捉

隐式捕捉

混合捕捉

捕捉列表的其他要点

2. lambda表达式的适用场景

总结


前言

        c++11 标准的发布为 c++ 带来了革命性的变化,引入了许多强大的新特性,使代码更简洁、高效且现代化。这些特性不仅提升了开发效率,还优化了性能,是现代 c++ 编程的重要基石。本篇文章,我们将重点探讨 c++11 的几个核心改进:列表初始化右值引用和移动语义类的新默认成员函数以及lambda表达式

一、列表初始化

1. 大括号初始化

        在c++98中,允许数组和结构体使用大括号初始化,例如:

#include <iostream>
using namespace std;struct STU
{int a;double b;
};int main()
{int arr[5] = { 1,2,3,4,5 };STU s = { 3,5.5 };return 0;
}

而在c++11之后,统一了初始化方式,“ 一切对象 ”均可以用大括号进行初始化(也叫做列表初始化),十分方便。举例:

int a = { 2 };
const STU& rs = { 3,5.5 }; //引用临时对象

对于自定义类型,使用大括号初始化本质是一种隐式类型转换,会产生临时对象,不过很多编译器会将其进行优化为直接构造对象。

注意:使用大括号初始化时,赋值符号可以省略。

int a{ 1 };
STU s{ 2,3.1 };
const STU& rs{ 3,5.5 };

c++11的大括号初始化方式,也在某些场景下带来了许多便利,例如调用容器接口操作多参数类型构造的对象时:

#include <iostream>
#include <vector>
#include <string>
using namespace std;int main()
{vector<pair<string, int>> v;//v.push_back(pair<string, int>("hello", 1)); //传统的方式--构造匿名对象v.push_back({ "hello", 1 }); //使用大括号构造pair,十分方便return 0;
}

2. initializer_list

        虽然刚才提到的大括号初始化已经十分方便,但是对于容器的多元素初始化,仍旧有些麻烦。所以c++11引入了一个新容器 -- initializer_list(初始化列表/初始化器),并在STL各种容器中增加了有它支持的构造函数、拷贝构造函数和赋值重载。代码示例:

#include <iostream>
#include <vector>
#include <list>
#include <map>
using namespace std;int main()
{vector<int> v = { 1,2,3,4,5 };list<double> l = { 3.1,5.2,4.8 };map<string, int> m = { {"hello", 1}, {"hehe", 3} };return 0;
}

这里赋值使用的大括号及其内部元素,即是initializer_list。它也有自己的定义方式:

//两种方式均可
auto il1 = { 1,2,3,4,5 };
initializer_list<int> il2 = { 1,2,3,4,5 };

initializer_list的本质是一个数组,其元素存放在栈区当中,它的迭代器是原生指针,内部有两个迭代器分别指向其开始位置和结束位置。我们使用它对容器进行构造或赋值时,是构造了一个initializer_list的对象,然后调用容器的相应函数去遍历initializer_list,一个个地构造。

二、右值引用和移动语义

        在某些情况下,使用传统的左值引用,并不能提高效率:

vector<string> v;
v.push_back("hello"); //“hello”是临时对象,不能直接进行引用

而在c++11当中,引入了一种新的引用--右值引用

1. 左值和右值

左值:一般是一个持久存在的表达式, 有明确内存地址,可以进行取地址操作。左值既可以放在赋值号左边,也可以放在赋值号右边。

右值:指那些临时性的,即将被销毁的,以及常量表达式,没有明确的内存地址,不能进行取地址操作。右值不能放在赋值号左边。

2. 左值引用和右值引用

        顾名思义,左值引用是给左值取别名,右值引用是给右值取别名。举个例子:

#include <iostream>
using namespace std;int main()
{int a = 0;int& r1 = a; //左值引用int&& r2 = 1; //右值引用return 0;
}

        左值引用不能直接引用右值,需要加上const;右值引用不能直接引用左值,需要将左值使用move函数包装起来(move将左值转化为右值引用)。示例:

#include <iostream>
using namespace std;int main()
{const int& r1 = 1; //左值引用引用右值int a = 0;//右值引用引用左值int&& r2 = (int&&)a; //强转int&& r3 = move(a); //使用move函数return 0;
}

需要注意:这里的 r2 和 r3 虽然是右值引用,但是 r2 和 r3 本身还是左值,是可以取到地址的。

        在语法层面上看,左值引用和右值引用都是给变量或常量取别名,不会开辟内存。但是从底层实现的角度上讲,左值引用和右值引用本质都是用指针实现的,与指针并没有什么区别。我们在学习语法的时候,语法所表达的意义与底层可能是背离的,尽量不要混到一起,容易陷入迷途。

引用延长生命周期

        const左值引用和右值引用都可以延长临时对象的生命周期,直至引用的生命周期结束,但const左值引用所引用的对象不可被修改;而通过右值引用,可以修改所引用的临时对象,因为右值引用本身是左值。

示例:

#include <iostream>
using namespace std;int main()
{const int& r1 = 1 + 3;r1++; // 报错,表达式必须是可修改的左值int&& r2 = 1 + 3;r2++; // 可以修改return 0;
}

左值引用和右值引用的参数匹配原则

        在c++98当中,被const修饰的左值引用形参既可以接收左值,也可以接收右值。而在c++11当中,由于引入了右值引用,所以当同时存在两个形参分别为右值引用和const的左值引用的重载函数时,如果我们传入左值,那么会优先调用const左值引用的重载函数;如果传入右值,则会优先调用右值引用版本的重载函数

代码示例:

#include <iostream>
using namespace std;//const左值引用版本
void func(const int& x)
{cout << "调用左值引用" << endl;
}//右值引用版本
void func(int&& x)
{cout << "调用右值引用" << endl;
}int main()
{int a = 0;func(a); // 传入左值func(1); // 传入右值func(move(a)); // 传入右值return 0;
}

注意:在右值引用版本的重载函数中,参数x的性质是左值(之前已经提到),所以如果要将x作为参数传入一个形参是右值引用的函数中,需要使用move或强转为右值引用类型。

        那么,右值引用存在的意义是什么呢?实际上右值引用的核心价值在于支持移动语义主要体现在移动构造和移动赋值这两个新默认成员函数中。我们具体分析一下。

右值引用的适用场景

        虽然说左值引用可以在对象的传参和返回时减少拷贝,提高效率。但如果对象在函数体内部创建,其离开函数体时就会销毁。如果要传值返回或强行传引用返回,就会发生拷贝/出现野引用

        此时右值引用的作用就体现出来了:右值引用可以接收即将被销毁的对象,再以左值的形式,在对象销毁之前,配合移动构造或移动赋值,将对象的资源直接移走,避免拷贝,从而提效

string类的移动构造和移动赋值实现示例:

//移动构造
//接收一个右值,通过右值引用转化为左值
string(string&& s)
{swap(s);//使用swap直接掠夺即将销毁的对象的资源
}//移动赋值
string& operator=(string&& s)
{swap(s);return *this;
}

有了移动构造,函数体内部定义的对象传值返回后,由于返回值是即将销毁的对象,会被移动构造给临时对象,临时对象再移动构造/移动赋值给当前接收对象,这样就完成了两次资源转移,在没有编译器优化的情况下也避免拷贝。

传参时,如果传入临时对象,也会通过移动构造直接将资源进行移动,免去了对象拷贝时的时间消耗。

注意:如果我们将左值经过move转化传给移动构造,那么左值的资源会被掠夺。

string s2 = move(s1); // 此时s1的资源被掠夺

引用折叠与万能引用

        c++不能直接定义引用的引用,但我们可以使用typedef的方式间接定义引用的引用,称之为引用折叠。示例:

#include <iostream>
using namespace std;typedef int& lr;
typedef int&& rr;int main()
{int a = 0;lr& r1 = a;lr&& r2 = a;rr& r3 = a;rr&& r4 = 1;return 0;
};

 这样就会产生四种甚至更多的引用类型,显得十分杂乱。为了使这种语法兼具实用性和简洁性,c++规定引用折叠的规则是:右值引用的右值引用表示右值引用,其余均表示左值引用所以这里的r1,r2,r3都是左值引用,r4是右值引用。

引用折叠的机制与模板相结合,产生了“万能引用”:

template<class T>
void func(T&& x)
{//...
}

当给func函数传入左值时,T会被实例化为左值引用类型,与右值引用折叠成左值引用;当传入右值时,T会被实例化为右值类型,与“&&”结合形成右值引用。

万能引用使得模板函数在接受参数时既能处理左值,也能处理右值,使代码更具灵活性和可扩展性。

完美转发

        如果我们给万能引用函数传入右值,由于右值引用变量本身是左值,所以如果用这个右值引用在内部嵌套调用其他函数,就无法保持参数的属性(传入的其实为左值),从而无法达到预期效果。为此C++提供了一个函数forward(完美转发),保持属性。

示例:

#include <iostream>
#include <utility>
using namespace std;void func2(int& x)
{cout << "左值版本" << endl;
}void func2(int&& x)
{cout << "右值版本" << endl;
}template<class T>
void func1(T&& x)
{func2(x); //预期是调用func2的右值引用版本,但x是左值func2(forward<T>(x));// 使用完美转发,传入右值
}int main()
{func1(1);return 0;
}

运行结果:

这里我们想要将func1的右值引用形参x传给func2,并调用func2的右值引用版本,但是x作为右值引用,语法规定其本身是左值类型,所以直接传入就会调用func2的左值引用版本。完美转发保持了x的右值属性,方便我们正确调用函数

        完美转发的本质是一个函数模板,它基于模板参数T的类型,决定是否使用move将左值转化为右值,从而保持原始参数的值属性,确保不会因为出现左值引用而产生不必要的临时对象拷贝,避免降低效率。

三、类的新默认成员函数

        c++11在原有的六个默认成员函数的基础上,新增了两个函数:构造函数和移动赋值函数。之前已经提到了它们面对临时对象拷贝时的优势,这里我们再系统介绍一下这两个函数的特点。

1. 移动构造函数

        移动构造函数是构造函数的一个重载,类似于拷贝构造函数,主要用于提高临时对象拷贝时的性能。它的特性如下:

1. 移动构造函数的第一个参数必须是该类类型的右值引用,如果还有其他参数,要求这些参数要有缺省值。

2. 如果我们没有显式实现移动构造函数,也没有显式实现析构函数、拷贝构造和赋值重载,那么编译器会默认生成一个移动构造函数。这个自动生成的移动构造函数进行移动构造时,如果遇到内置类型成员,会逐个拷贝;遇到自定义类型的成员,则会调用该成员的移动构造。如果该成员没有移动构造,则会调用其拷贝构造

//移动构造
string(string&& s)
{swap(s);
}

2. 移动赋值函数

        与赋值重载类似,移动赋值对已经存在的对象的赋值,并且会将对方的资源移走。它的特性:

1. 移动赋值必须重载为成员函数,且其第一个参数必须是该类类型的右值引用,一般情况下,将返回值类型设置为该类类型的左值引用,减少拷贝提高效率。

2. 如果我们没有显式实现移动赋值函数,也没有显式实现析构函数、拷贝构造和赋值重载,那么编译器会默认生成一个移动赋值函数。这个自动生成的移动赋值函数进行移动赋值时,如果遇到内置类型成员,会逐个拷贝;遇到自定义类型的成员,则会调用该成员的移动赋值。如果该成员没有移动赋值,则会调用其赋值重载

//移动赋值
string& operator=(string&& s)
{swap(s);return *this;
}

注意:对于内部需要动态申请资源,要进行资源深拷贝的类,移动构造和移动赋值才有意义,因为移动构造和移动赋值的本质是“窃取”临时对象的资源,而不是像拷贝构造或赋值重载那样去完成资源的深拷贝,这样才能提高效率。

四、lambda表达式

        lambda表达式与仿函数类似,是c++11新引入的一种匿名函数对象。与普通函数不同的是,它可以定义在函数体内部。

它的语法格式如下:

[捕捉列表](参数列表)->返回值类型{函数体}

示例:

#include <iostream>
using namespace std;int main()
{//简单的lambda表达式auto f = [](int x, int y)->int {return x + y; };//调用cout << f(1, 2) << endl;return 0;
}

 

注意:lambda表达式的类型在语法层面是不具体的,所以我们一般用auto或者模板参数定义的对象类型去接收lambda表达式

这里使用 f 接收lambda表达式以后,我们就可以像调用函数一样调用 f ,并且进行传参。

在定义lambda表达式时,有如下规则需要我们遵守:

1. 捕捉列表的“[ ]”不能省略。

2. lambda表达式的参数为空时,参数列表(包括“( )”)可以省略。

3. 返回值类型(包括“->”)可以省略。

4. 若返回值类型已写,那么就算没有参数,参数列表的“( )”也不能省略。

5. 函数体的“{ }”不能省略。

 lambda表达式中的参数列表、返回值类型函数体都很好理解(与普通函数的含义相同),但是这里的捕捉列表是什么呢?

1. 捕捉列表

        就像函数一样,lambda表达式的函数体只能使用其对应参数列表中的变量,无法使用到外部变量。而捕捉列表可以让我们在lambda表达式的函数体当中使用外层变量

捕捉一共分为三种,分别是显式捕捉、隐式捕捉和混合捕捉

显式捕捉

        显式捕捉就是在捕捉列表当中显式写明要在函数体中使用的变量。示例:

#include <iostream>
using namespace std;int main()
{int a = 1;int b = 3;auto f = [a](int x){//return a + b + x; // 报错,b不能使用return a + x; //捕捉列表中写明了a,可以使用a};cout << f(1) << endl;return 0;
}

当然,如果我们要在函数体内部修改变量a的值,是不允许的(因为这里的a是传值捕捉)。如果要修改a的值,那么需要传引用捕捉

auto f = [&a](int x) // a前面加一个&

隐式捕捉

        相比显式捕捉,隐式捕捉就显得十分便利:它的机制是函数体内部使用了哪些变量就捕捉哪些变量。 隐式捕捉也分为传值捕捉和传引用捕捉

auto f1 = [=] {}; // 隐式的传值捕捉
auto f2 = [&] {}; // 隐式的传引用捕捉

使用了隐式捕捉之后,我们就可以在函数体内部任意使用外部的变量,编译器会根据我们使用的变量自动捕捉它们。

混合捕捉

        如果我们需要在隐式捕捉的基础上,确保其中某些值可以被修改或不能被修改,那么就可以使用混合捕捉。示例如下:

auto f1 = [=, &a] {}; // 其他变量使用传值捕捉,a使用传引用捕捉
auto f2 = [&, a] {}; // 其他变量使用传引用捕捉,a使用传值捕捉

注意:混合捕捉当中,捕捉列表的第一个位置必须是“=”或“&”;如果混合捕捉的隐式部分使用了传值捕捉,那么其他特定变量就不能设置为传值捕捉(没必要),传引用捕捉同上。


捕捉列表的其他要点

1. 全局变量静态变量不能捕捉,也没必要捕捉,可以直接使用。

2. 对于传值捕捉,其本质是将外层数据进行拷贝,并加上const进行修饰,所以函数体内部没办法修改它。如果想要修改捕捉的值,而不影响外部,可以在参数列表之后,返回值类型之前加上关键字mutable示例:

#include <iostream>
using namespace std;int main()
{int a = 0;auto f = [=]() mutable ->int //这里的返回值类型可以省略{a++; //可以修改a,且不影响外部的areturn a;};return 0;
}

注意:加上关键字mutable后,那么就算没有参数,参数列表的“( )”也不能省略。

2. lambda表达式的适用场景

        在lambda表达式出现之前,可调用对象只有函数指针仿函数两种,但它们定义起来都比较麻烦。lambda和它们的作用类似,功能也十分强大,在某些场景下编码也会更加方便(因为它是匿名函数对象)。 示例:

sort(a.begin(), a.end(), [](const A& a1, const A& a2){//直接定义排序规则});

除此之外,lambda在线程中定义线程的执行函数逻辑智能指针的删除器定义等场景中也十分适用。

总结

        本篇文章,我们主要学习了c++11的部分新特性:列表初始化、右值引用、类的新默认成员函数和lambda表达式。通过剖析引用折叠、完美转发等底层机制,lambda的多种捕捉方式与适用场景,希望能帮助大家在资源管理、代码简洁性及性能提升之间找到平衡。之后博主会和大家分享c++11其他的新特性,如可变参数模板、包装器、智能指针等。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

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

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

相关文章

软考中级-软件设计师 2022年下半年上午题真题解析:通关秘籍+避坑指南

&#x1f4da; 目录&#xff08;快速跳转&#xff09; 选择题&#xff08;上午题&#xff09;&#xff08;每题1分&#xff0c;共75分&#xff09;一、 计算机系统基础知识 &#x1f5a5;️&#x1f4bb; 题目1&#xff1a;计算机硬件基础知识 - RISC&#xff08;精简指令集计算…

基于MCP协议调用的大模型agent开发02

目录 在AI agent的开发过程中&#xff0c;如何使用mcp服务器作为大模型的工具调用‘百宝箱’&#xff1f; FastAPI FastMCP 本系列&#xff1a; 基于MCP协议调用的大模型agent开发01-CSDN博客 基于MCP协议调用的大模型agent开发02-CSDN博客 在AI agent的开发过程中&#xff0c;…

ES6(8) Fetch API 详解

1. Fetch API 简介 fetch 是 ES6 提供的基于 Promise 的 API&#xff0c;用于发送 HTTP 请求并处理服务器响应数据。与传统的 XMLHttpRequest 相比&#xff0c;fetch 语法更加简洁&#xff0c;使用 Promise 进行异步处理&#xff0c;避免了回调地狱。 1.1 fetch() 的基本用法 …

原生SSE实现AI智能问答+Vue3前端打字机流效果

实现流程&#xff1a; 1.用户点击按钮从右侧展开抽屉&#xff08;drawer&#xff09;&#xff0c;打开模拟对话框 2.用户输入问题&#xff0c;点击提问按钮&#xff0c;创建一个SSE实例请求后端数据&#xff0c;由于SSE是单向流&#xff0c;所以每提一个问题都需要先把之前的实…

CUDA 工具链将全面原生支持 Python

根据 NVIDIA 在 2025 年 GTC 大会上的官宣&#xff0c;CUDA 工具链将全面原生支持 Python 编程&#xff0c;这一重大更新旨在降低 GPU 编程门槛&#xff0c;吸引更广泛的 Python 开发者进入 CUDA 生态。以下是核心信息整合&#xff1a; 1. 原生支持的意义与背景 无需 C/C 基础…

jupyter notebook 显示conda虚拟环境

使用 nb_conda_kernels 安装 nb_conda_kernels&#xff1a;这个包可以自动从你的 Conda 环境中发现并列出内核。 conda activate base # 确保你在 base 环境或任何其他环境中安装 conda install nb_conda_kernels显示jupyternotebook当前所在的位置。

【AI】MCP概念

一文讲透 MCP&#xff08;附 Apifox MCP Server 内测邀请&#xff09; 7分钟讲清楚MCP是什么&#xff1f;统一Function calling规范&#xff0c;工作量锐减至1/6&#xff0c;人人手搓Manus&#xff01;&#xff1f; | 一键链接千台服务器&#xff0c;几行代码接入海量外部工具…

WSL1升级到WSL2注意事项

今天要在WSL上安装docker&#xff0c;因为机器上安装了wsl1&#xff0c;docker安装后启动不了&#xff0c;通过询问deepseek发现docker只能在wsl2上安装&#xff0c;因此就想着将本机的wsl1升级到wsl2。 确保你的 Windows 系统是 Windows 10&#xff08;版本 1903 及以上&…

Pycharm常用快捷键总结

主要是为了记录windows下的PyCharm的快捷键&#xff0c;里面的操作都试过了功能描述会增加备注。 文件操作 快捷键功能描述Ctrl N新建文件Ctrl Shift N根据名称查找文件Ctrl O打开文件Ctrl S保存当前文件Ctrl Shift S另存为Alt F12打开终端&#xff08;Terminal&…

电池分选机:新能源时代的品质守护者|深圳比斯特自动化

在这个新能源蓬勃发展的时代&#xff0c;电池作为能量的存储与释放单元&#xff0c;其性能与质量直接关系到整个系统的稳定运行与效率提升。而电池分选机&#xff0c;作为电池生产流程中的关键一环&#xff0c;正扮演着品质守护者的角色&#xff0c;为新能源产业的高质量发展保…

认识 Linux 内存构成:Linux 内存调优之虚拟内存与物理内存

写在前面 博文内容涉及 Linux 内存构成基本认知包括虚拟内存和物理内存映射,多级页表和MMU简单认知理解不足小伙伴帮忙指正对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的…

SCI科学论文的重要组成部分

科学论文的核心结构 科学论文通常遵循IMRAD结构&#xff0c;即&#xff1a; 引言(Introduction)方法(Methods)结果(Results)讨论(Discussion) 除此之外&#xff0c;还包括其他几个关键部分。让我为您详细介绍每个部分的作用和重要性&#xff1a; 1. 标题(Title) 标题是论文…

期权时间价值与隐含波动率怎么选?

期权隐含波动率与时间价值要怎么选&#xff1f;期权隐含波动率IV对期权价格有着巨大的影响。整体来看&#xff0c;期权隐波与期权价格呈正相关关系。当期权隐波从低水平上升时&#xff0c;期权价格也会相应上涨&#xff1b;反之&#xff0c;当隐波下降&#xff0c;期权价格则会…

STM32 HAL库扩大USB CDC的输入缓冲区

STM32 HAL库,使用USB, 扩大输入暂存区的方法 使用STM32的USB通讯CubeMX建立配置Serial Wire时钟配置USB配置时钟频率设置代码编写运行效果总结使用STM32的USB通讯 STM32可以不用使用串口转换直接和USB通讯。这给串口调试提供了极大的方便。编程,我使用了STM32CubeIDE编程。这…

ffmpeg函数简介(封装格式相关)

文章目录 &#x1f31f; 前置说明&#xff1a;FFmpeg 中 AVFormatContext 是什么&#xff1f;&#x1f9e9; 1. avformat_alloc_context功能&#xff1a;场景&#xff1a; &#x1f9e9; 2. avformat_open_input功能&#xff1a;说明&#xff1a;返回值&#xff1a; &#x1f9…

费马小定理

快速幂 理论 a n a a ⋯ a a^n a a \cdots a anaa⋯a&#xff0c;暴力的计算需要 O(n) 的时间。 快速幂使用二进制拆分和倍增思想&#xff0c;仅需要 O(logn) 的时间。 对 n 做二进制拆分&#xff0c;例如&#xff0c; 3 13 3 ( 1101 ) 2 3 8 ⋅ 3 4 ⋅ 3 1 3^{13}…

ADGaussian:用于自动驾驶的多模态输入泛化GS方法

25年4月来自香港中文大学和浙大的论文“ADGaussian: Generalizable Gaussian Splatting for Autonomous Driving with Multi-modal Inputs”。 提出 ADGaussian 方法&#xff0c;用于可泛化的街道场景重建。所提出的方法能够从单视图输入实现高质量渲染。与之前主要关注几何细…

js中this指向问题

在js中&#xff0c;this关键字的指向是一个比较重要的概念&#xff0c;它的值取决于函数的调用方式。 全局状态下 //全局状态下 this指向windowsconsole.log("this", this);console.log("thiswindows", this window); 在函数中 // 在函数中 this指向win…

我的NISP二级之路-03

目录 一.ISMS 二.IP 三.http 四.防火墙 五.文件 解析 解析 六.攻击 解析 解析 七.风险管理工程 八.信息系统安全保护等级 九.我国信息安全保障 一.ISMS 1.文档体系建设是信息安全管理体系(ISMS)建设的直接体现&#xff0c;下列说法不正确的是&#xff1a; A&#…

HarmonyOS应用开发者高级-编程题-001

题目一&#xff1a;跨设备分布式数据同步 需求描述 开发一个分布式待办事项应用&#xff0c;要求&#xff1a; 手机与平板登录同一华为账号时&#xff0c;自动同步任务列表任一设备修改任务状态&#xff08;完成/删除&#xff09;&#xff0c;另一设备实时更新任务数据在设备…