当前位置: 首页 > news >正文

【C++】模板进阶

文章目录

  • 一、非类型模板参数
    • 1、非类型模板参数
    • 2、C++11 中的 array 类
  • 二、模板的特化
    • 1、模板特化的概念
    • 2、函数模板特化
    • 3、类模板特化
      • 3.1 全特化
      • 3.2 偏特化
  • 三、模板的分离编译
  • 四、模板总结

一、非类型模板参数

1、非类型模板参数

模板参数分为类型形参与非类型形参,类型形参即出现在模板参数列表中,跟在 class 或者 typename 关键字之后的参数类型名称,我们前面使用的所有模板参数都是类型形参;而非类型形参则是用一个常量作为类模板/函数模板的一个参数,在类模板/函数模板中可将该参数当成常量来使用。

我们以静态数组为例;在没有非类型模板参数时,我们采用如下方式来定义一个静态数组:

#define N 10
template<class T>
class arr {
public://...
private:T _a[N];
};void test1() {arr<int> arr;
}

但是这样有一个缺陷,因为 N 的大小是固定的,所以我们只能定义出 N 个元素大小的 arr对象,那么此时如果我们想要同时定义两个大小为 10 和 100 的数组显然是做不到的。

C++ 中设计了非类型模板参数来解决了这个问题,如下,我们可以通过传递不同的非类型形参来定义不同的类,非类型模板参数在函数模板中的使用也是如此:

template<class T, size_t N>
class arr {
public://...
private:T _a[N];
};void test1() {arr<int, 10> arr1;arr<double, 100> arr2;
}

注意事项:

  • 非类型模板参数也可以给定缺省值;
  • 非类型模板参数只适用于整形,不适用于浮点数、类对象以及字符串;
  • 非类型的模板参数必须在编译期间就能确认结果,即非类型模板参数的实参只能是常量。

2、C++11 中的 array 类

C++ 11 中引入了一个新类 – array,array 使用非类型形参作为模板参数,其底层其实就相当于静态数组:image-20230306235419932

由于 array 底层是静态数组,所以 array 的特性和静态数组的特性一样 – 数据存放在栈上、不要求数据连续存储 (使用 [] 给指定位置的数据赋值)、也不允许 push_back (不能扩容):array 使用文档image-20230306235754776

有的同学可能有疑问 – 既然 array 的底层就是静态数组,那为什么不直接使用C语言中的静态数组,而要将它封装成为一个新类呢?答案是为了更严格的进行越界检查

C语言静态数组对越界的检查规则是 – 越界读不检查、越界写抽查:image-20230307000421989

image-20230307000548336

image-20230307000621772

而 array 里面重载了 [] 运算符,在 operator[]() 函数里面对数组边界进行了判断,所以不管是越界读还是越界写都会检查出来:image-20230307000903730

image-20230307000935767

所以,其实 C++ 11 设计出 array 类是为了让 array 替代掉C语言的静态数组,以此来帮助人们更早的发现并解决程序中可能出现的越界问题,但是由于人们数组已经用习惯了,所以 array 其实被使用的并不多。

注:array 除了具有安全性,还有可读性、抽象性、兼容性等优点。


二、模板的特化

1、模板特化的概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理;比如,实现了一个专门用来进行小于比较的函数模板:

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}void test4()
{cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误
}

image-20230307145617414

可以看到,Less 绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果;上述示例中,p1 指向的 d1 显然小于 p2 指向的d2 对象,但是 Less 内部并没有比较 p1 和 p2 指向的对象内容,而比较的是 p1 和 p2 指针的地址,这就无法达到预期而发生错误。

此时,就需要对模板进行特化 – 即在原模板类的基础上,针对特殊类型进行特殊化的处理;模板特化中分为函数模板特化与类模板特化

2、函数模板特化

函数模板特化的步骤如下:

  1. 必须要先有一个基础的函数模板;
  2. 关键字 template 后面接一对空的尖括号<>;
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型;
  4. 函数形参表必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

如下:

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}//对Date*类型进行模板特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;  //特殊处理:比较left和right指向的内容,而不是left和right本身
}

image-20230307151434767

有的同学可能会说,我直接重载一个参数类型为 Date* 的函数即可,为什么要费这么大劲搞成模板的特化呢?-- 确实,由于函数支持重载,所以我们完全可以将重载一个/多个特殊类型的形参;所以,一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出 (函数重载)

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}//Date*类型的函数重载
bool Less(Date* left, Date* right)
{return *left < *right;  //特殊处理:比较left和right指向的内容,而不是left和right本身
}

image-20230307182044060

如上,对于一些参数类型复杂的函数模板直接给出,即实现为函数重载,这种方法该种实现简单明了,代码的可读性高,容易书写,因此函数模板不建议特化

3、类模板特化

类模板特化又分为全特化与偏特化。

3.1 全特化

全特化即是将模板参数列表中所有的参数都确定化,如下:

template<class T1, class T2>
class Data {
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};template<>
class Data<int, char> {  //类模板全特化 
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};

image-20230307183259214

3.2 偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。偏特化有以下两种表现方式:

  • 部分特化 – 将模板参数类表中的一部分参数特化;
  • 参数更进一步的限制 – 偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

我们以如下类模板为例:

template<class T1, class T2>
class Data {
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};

部分特化:

//部分特化--将第二个参数特化为int
template <class T1>
class Data<T1, int> {
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};void test6() {Data<int, char> d1;  //使用普通模板实例化Data<int, int> d2;  //第二个参数与模板特化中的特化参数相同,优先使用特化模板进行实例化
}

可以看到,我们可以将模板中的部分参数显示指定为某种具体类型,这样模板参数在进行匹配时会优先匹配。image-20230308212117568

参数更进一步限制:

//参数更进一步限制--两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*> {
public:Data() { cout << "Data<T1*, T2*>" << endl; }
private:T1 _d1;T2 _d2;
};//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&> {
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}
private:const T1& _d1;const T2& _d2;
};void test7() {Data<double, int> d1; // 调用特化的int版本Data<int, double> d2; // 调用基础的模板Data<int*, int*> d3; // 调用特化的指针版本Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

可以看到,我们可以通过偏特化对模板参数进行进一步的限制,比如模板参数定义为 <T*, T*>,这样只要实参为指针类型,不管是 int*、double*、还是 vector<int>*,都会调用此特化模板;又比如将模板参数定义为为 <T&, T&>,这样只要实参是引用类型就会调用此特化模板;

从而实现了在限制参数类型的同时又不会将参数局限为某一种具体类型。image-20230308212904779


三、模板的分离编译

阅读我博客的同学会发现,自从学习了模板以后,凡是要用到模板的类我们成员函数的声明和定义都是放在一起的,或者是直接在类中给出函数的定义,而不提供函数的声明,比如我们模拟实现的 vector、list、stack、queue、priority_queue 等容器;

那为什么我们不像C语言或者非模板类那样将类成员函数的声明和定义进行分离呢?函数声明和定义分离不是即能够方便别人阅读我们的代码,还能够保护源码不被泄露吗?-- 这是因为模板不支持分离编译

我们以简易版的 stack 为例:

//Stack.h
#include<iostream>
using namespace std;template<class T>
class Stack
{
public:Stack(int capacity);~Stack();void push(const T& x);private:T* _a;int _top;int _capacity;
};
//Stack.cpp
#include "stack.h"template<class T>
Stack<T>::Stack(int capacity)
{cout << "Stack(int capacity = )" << capacity << endl;_a = (T*)malloc(sizeof(T) * capacity);if (_a == nullptr){perror("malloc fail");exit(-1);}_top = 0;_capacity = capacity;
}template<class T>
Stack<T>::~Stack()
{cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;
}template<class T>
void Stack<T>::push(const T& x)
{// ....//_a[_top++] = x;
}

如上,我们将模板类 stack 进行声明和定义分离,注意:

1、类模板的外部成员定义不得具有默认参数,即类模板声明与定义分离时不能成员函数不能使用缺省参数;

2、类模板的成员函数在分离定义时必须指明该函数是属于那个类的,而 stack 是类名,stack<T> 才是类型,所以我们需要在每个成员函数前面指明类类型 stack<T>。

但是当我们编译运行的时候我们发现分离定义的所有成员函数都出现了链接性错误image-20230308223050705

造成这种错误的原因如下:

在C语言 程序环境和预处理 那一节我们学习了一个 .c/.cpp 程序要变成 .exe 可执行程序一共要经历四个步骤,分别是预处理、编译、汇编和链接,这期间它们的工作分别为:

  • 预处理:注释的删除,#define 定义的符号、宏的替换以及删除,各种条件编译的处理,头文件的展开;
  • 编译:进行语法分析、词法分析、语义分析和符号汇总;
  • 汇编:生成符号表;
  • 链接:合并段表、符号表的合并和重定位。

同时,预处理、编译、汇编这几个阶段每个源文件 (.c 文件) 都是独立进行的,只有在链接时才会将这几个目标文件合并到一起形成可执行程序。image-20230308224940400

在了解了这些知识以后,我们就可以得出程序报错的原因了:

1、预处理时,Stack.h 头文件分别展开到 Stack.cpp 和 Test.cpp 源文件中;

2、经过编译,Stack.cpp 和 Test.cpp 分别转变成汇编代码;

3、在汇编时,由于 Test.cpp 里面只有 Stack、~Stack、push 等函数的声明,而没有其定义,所以 Test.cpp 生成的符号表会给这些函数对应一个无效地址;同时,由于 Stack.cpp 里面并没有对模板实例化的代码,即没有 Stack<int>,也就没有生成具体的代码,所以 Stack.cpp 的符号表里面函数对应的也是无效地址;

4、在链接时,需要将 Test.cpp 和 Stack.cpp 符号表中的内容进行合并与重定位,但是由于它们符号表中的都是无效地址,所以发生链接错误。

在找出错误原因后有的同学可能会说,这简单,在 Stack.cpp 中对模板进行显式实例化即可,如下:

//Stack.cpp 中增加显式实例化的代码
template
class Stack<int>;

这样做确实可以,但是如果我们此时还要定义一个 double 类型的对象呢?那么我们又需要将 Stack.cpp 中的显式实例化类型改为 double,也就是说,在同一份代码中我们只能定义同一种类型的对象,那么这样也就失去了模板原本的意义了。image-20230308230348030

所以,模板不支持分离编译,我们一般采用其他的解决办法,如下:

1、模板函数不进行声明,直接在类里面给出函数的定义;(如果类很大时这种方法不方便别人阅读我们的代码,不推荐使用;当类较小时可以这样做,比如我们之前模拟实现的 STL 容器)

2、将模板函数的声明和定义放到同一个文件 “xxx.hpp” 中 (hpp:.h + .cpp) 。(这种方式使用于类较大时,方便别人快速了解我们的类)

3、注:这两种方法都有一个缺点 – 会暴露源码,因为函数的声明和定义是在一个文件中的,我们将类提供给别人使用时不得不将源码也暴露给别人,这也是模板的一个缺点。

四、模板总结

模板的优点:

  • 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库 (STL) 因此而产生;
  • 模板增强了代码的灵活性。

模板的缺点:

  • 模板会导致代码膨胀问题,也会导致编译时间变长;
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误;

相关文章:

【C++】模板进阶

文章目录一、非类型模板参数1、非类型模板参数2、C11 中的 array 类二、模板的特化1、模板特化的概念2、函数模板特化3、类模板特化3.1 全特化3.2 偏特化三、模板的分离编译四、模板总结一、非类型模板参数 1、非类型模板参数 模板参数分为类型形参与非类型形参&#xff0c;类…...

三板斧解决leetcode的链表题

在《波奇学单链表》中我们提到单链表的两个特点单向性。头节点尾节点的特殊性导致分类讨论的情况。如何看单链表&#xff1f;让我们简化成下图cur表示当前节点&#xff0c;下图表示cur移动&#xff0c;圆圈表示值用哨兵卫节点(新的头节点)和把尾节点看成NULL来把头尾节点一般化…...

全生命周期的云原生安全框架

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/129423036 一、全生命周期的云原生安全框架 如图所示&#xff1a; 二、框架说明 在上图中&#xff0c;我们从两个维度描述各个安全机制&#xff0c;横轴是开发和运营阶段&#xff0c;细分为编码、测试…...

【本地网站上线】ubuntu搭建web站点,并内网穿透发布公网访问

【本地网站上线】ubuntu搭建web站点&#xff0c;并内网穿透发布公网访问前言1. 本地环境服务搭建2. 局域网测试访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名4.3 测试访问公网固定二级子…...

电脑怎么重装系统?教你轻松掌握这些方法

重新安装计算机系统有两种原因&#xff1a;一种是计算机系统可以正常使用&#xff0c;但是电脑比较卡&#xff0c;为了提高它的运行速度&#xff0c;所以想要通过重新安装系统来解决这个问题;另一种原因是计算机系统文件丢失&#xff0c;系统出现蓝屏&#xff0c;或者黑屏的情况…...

leetcode-每日一题-2379(简单,字符串)

久违的简单题......给你一个长度为 n 下标从 0 开始的字符串 blocks &#xff0c;blocks[i] 要么是 W 要么是 B &#xff0c;表示第 i 块的颜色。字符 W 和 B 分别表示白色和黑色。给你一个整数 k &#xff0c;表示想要 连续 黑色块的数目。每一次操作中&#xff0c;你可以选择…...

SLF4J日志框架在项目中使用

介绍 SLF4J全称“Simple Logging Facade for Java”&#xff0c;作为各种日志框架的简单门面。例如&#xff1a; java.util.logging、logback 、 reload4j等。只需要切换日志框架的jar包依赖就可以切换日志框架。 SLF4J支持的日志框架包含如下&#xff1a; log4j&#xff1a…...

Spark MLlib 模型训练

Spark MLlib 模型训练决策树随机森林GBDTSpark MLlib 开发框架下 : 监督学习 : 回归 (Regression) , 分类 (Classification) , 协同过滤 (Collaborative Filtering)非监督学习 : 聚类 (Clustering) 、频繁项集 (Frequency Patterns) 例子分类 : 算法分类 : 算法分类算法子分类…...

Python中变量的作用域精讲

文章目录前言一、局部变量二、全局变量前言 变量的作用域是指程序代码能够访问该变量的区域&#xff0c;如果超出该区域&#xff0c;再访问时就会出现错误。在程序中&#xff0c;一般会根据变量的 “有效范围” 将变量分为 “全局变量” 和 “局部变量”。 一、局部变量 局部变…...

数据仓库工程师的工作职责的相关介绍

1. BI 开发工程师的工作内容是什么&#xff1f; BI开发工程师&#xff08;Business Intelligence Developer&#xff09;是负责设计和开发企业级BI系统的专业人员。他们的主要工作是从多个数据源中提取、转换、加载和分析数据&#xff0c;以支持企业决策。以下是BI开发工程师的…...

ESP UART 介绍

1 UART 介绍 UART 是一种以字符为导向的通用数据链&#xff0c;可以实现设备间的通信。异步传输的意思是不需要在发送数据上添加时钟信息。这也要求发送端和接收端的速率、停止位、奇偶校验位等都要相同&#xff0c;通信才能成功。 1.1 UART 通信协议 一个典型的 UART 帧开始…...

第十三届蓝桥杯省赛Python大学B组复盘

目录 一、试题B&#xff1a;寻找整数 1、题目描述 2、我的想法 3、官方题解 4、另解 二、试题E&#xff1a;蜂巢 1、题目描述 2、我的想法 3、官方题解 三、试题F&#xff1a;消除游戏 1、题目描述 2、我的想法&#xff08;AC掉58.3%&#xff0c;剩下全超时&#x…...

linux入门---vim的配置

这里写目录标题预备知识如何配置vimvim一键配置预备知识 在配置vim之前大家首先得知道一件事就是vim的配置是一人一份的&#xff0c;每个用户配置的vim都是自己的vim&#xff0c;不会影响到其他人&#xff0c;比如说用户xbb配置的vim是不会影响到用户wj的&#xff0c;虽然不同…...

Python简写操作(for、if简写、匿名函数)

Python简写操作&#xff08;for、if简写、匿名函数&#xff09;1. for 简写1.1 一层 for 循环1.2 两层 for 循环2. if 简写3. for 与 if 的结合简写4. 匿名函数 lambda1. for 简写 举个例子&#xff1a; y [1, 2, 3, 4, 5, 6] result [(i * 2) for i in y] print(result)# …...

毕业设计常用模块之温湿度模块DHT11模块使用

DHT11是一款可以测量温度数据和湿度数据的传感器 产品特点 暖通空调、除湿器、农业、冷链仓储、测试及检测设备、消费品、汽车、自动控制、数据记录器、气 象站、家电、湿度调节器、医疗、其他相关湿度检测控制 外形尺寸 第3管脚&#xff1a;NC 是没有用的 典型电路 通信方式…...

Cadence Allegro 导出Design Rules Net Shorts Check(DRC)Report报告详解

⏪《上一篇》   🏡《上级目录》   ⏩《下一篇》 目录 1,概述2,Design Rules Net Shorts Check(DRC)Report作用3,Design Rules Net Shorts Check(DRC)Report示例4,Design Rules Net Shorts Check(DRC)Report导出方法4.1,方法14.2,方法2...

第 46 届世界技能大赛浙江省选拔赛“网络安全“项目C模块任务书

第46届世界技能大赛浙江省选拔赛"网络安全"项目C模块&#xff08;夺旗行动&#xff08;CTF&#xff09;挑战&#xff09;第46届世界技能大赛浙江省选拔赛"网络安全"项目C模块第一部分 WEB第二部分 CRYPTO第三部分 REVERSE第四部分 MISC第五部分 PWN第46届世…...

C++:详解C++11 线程(一):MingGW 各版本区别及安装说明

MingGW 各版本区别一&#xff1a;MinGW、MinGW-w64 简介二&#xff1a;MinGW 各版本参数说明三&#xff1a;下载解压一&#xff1a;MinGW、MinGW-w64 简介 MinGW&#xff08;全称为 Minimalist GNU for Windows&#xff09;&#xff0c;它实际上是将经典的开源 C 语言编译器 G…...

第十二章 ArrayList和 LinkedList的区别

ArrayList&#xff1a;基于动态数组&#xff08;自动扩容&#xff09;&#xff0c;连续内存存储&#xff0c;由于底层是数组&#xff0c;适合使用下标进行访问&#xff0c;但扩容一直都是数组的缺点&#xff0c;所以使用尾插法进行扩容可以有效提高扩容效率。还有就是创建Array…...

案例06-复用思想的接口和SQL

目录 一&#xff1a;背景介绍 二&#xff1a;思路&方案 三&#xff1a;过程 1.Controller层接口的复用 2.Mapper层sql语句的复用 四&#xff1a;总结 一&#xff1a;背景介绍 我们在开发项目的过程中非常容易出现的一种现象就是用什么我就直接写什么&#xff0c;就像我…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...