C++函数指针详解

简介: C++函数指针详解

本文详细介绍了C/C++中的普通函数和类的成员函数的指针。结合C++代码示例讲解了函数指针作为其他函数的输入、返回值以及typedef如何提高代码可读性的实用技巧。对于类的成员函数(方法)指针,则分为静态和非静态两种情况。最后总结了普通函数、类的非静态成员函数、类的静态成员函数的声明、赋值和定义的C++语法以供查阅。
普通函数的指针
声明、定义和赋值

首先让我们来区别以下4条声明语句:

int x; // x是一个int型的变量
int x; // x是一个指向int型变量的指针
int
x(); // x是一个返回int型指针的函数
int (*x)(); // x是一个函数指针,输入参数为空,返回类型为int型指针

因此,把一个名为fun的函数的声明语句变为函数指针变量pfun的声明语句,只需要将 fun 变为 (*pfun),其他的不变即可(注意:小括号必不可少!)。

double fun(string& str1, string &str2); // 函数fun的声明
double (*pfun)(string& str1, string &str2); // 函数指针变量pfun的声明

在C/C++中,数组变量的名字是一个指向该数组元素类型的指针常量(存放首元素的地址),类似地,函数的名字也表示一个指向该函数的指针常量(存放该函数入口地址):

int array[5]; // array是一个指向int的指针常量
int *p = array; // p是一个指向int的指针变量,p = array合法
p[3]; // 等价于array[3]

void test(int a){ cout << "a=" << a << endl;} // test是一个函数指针常量,输入参数int,返回类型void
void (*pf)(int); // 声明函数指针pf: pf是一个输入参数int,返回void的函数的指针
pf = test; // 将test函数地址赋值给pf(二者输入参数列表、返回类型必须一致)
pf(123); // 等价于test(123)

有几点需要说明:

// 声明和赋值可以写在一起:
void (*pf)(int) = test;

// 2种赋值方式:(等价)
pf = test; // 直接函数名赋值
pf = &test; // 函数名取地址赋值,等价于pf = test

// 2种调用方式:(等价)
pf(123); // 直接pf接参数调用
(*pf)(123); // 先对函数指针解引用,再接参数调用,等价于pf(123)

例1:

include

using namespace std;

int add(int x, int y){
return x + y;
}

int substract(int x, int y){
return x - y;
}

int main(){
int (fp)(int, int); // 定义函数指针变量:fp
fp = add; // 将同类型函数add赋值给fp
cout << fp(1, 3) << endl;
cout << (
fp)(1, 3) << endl; // 等价于fp(1,3)
fp = &substract; // 将同类型函数substract赋值给fp, 取地址符号"&"可加可不加
cout << fp(1, 3) << endl;
cout << (*fp)(1, 3) << endl; // 等价于fp(1,3)
return 0;
}

输出:

4
4
-2
-2

重载的函数自动匹配类型

如果有同名的重载函数,编译器会自动选择类型匹配的一个:

include

using namespace std;

int add(int x, int y){
return x + y;
}

int add(int x, int y, int z){ // 重载
return x + y + z;
}

int main(){
int (*fp)(int, int) = add; // 编译器会自动选择add(int x, int y)与fp匹配
cout << "1 + 2 = " << fp(1,2) << endl;

int (*fp2)(int, int, int) = add;  // 编译器会自动选择add(int x, int y, int z)与fp2匹配
cout << "1 + 2 + 3 = " << fp2(1,2,3) << endl;

return 0;

}

输出:

1 + 2 = 3
1 + 2 + 3 = 6

类型复杂的函数指针和typedef的妙用

当一个函数的输入列表、返回列表较为复杂时,尤其是参数/返回值中还嵌套了函数指针时,代码的可读性将会大大下降。
1、输入参数包含函数指针

函数指针作为输入参数(形参)有两种写法:将输入参数int变为int (pf)(int, int),表示输入参数为函数指针pf(指向函数类型为int (pf)(int, int))。

void test(int fun(int, int)) // 直接把fun函数的声明写在参数的位置,在test内部fun作为形参变量,表示函数指针
void test(int (*pf)(int, int)) // 将函数指针的声明写在参数的位置,在test内部pf作为形参变量,与上面写法等价

例2:下面例子中的add2()和add3() 就是输入参数为函数声明/函数指针的两种等价写法。

include

using namespace std;

int add(int x, int y){ // 双参数函数add2:两个数相加
return x + y;
}

int add2(int (*fun)(int, int), int x, int y, int z){ // 使用函数声明语句作为形参,构造三参数函数add2()
return fun(fun(x,y),z);
}

int add3(int fun(int, int), int x, int y, int z){ // 与上面等价, 使用函数指针作为形参,构造三参数函数add3()
return fun(fun(x,y),z);
}

int main(){
int (*fp)(int, int);
fp = add;
cout << "1 + 2 = " << fp(1,2) << endl;

int (*fp2)(int (*fun)(int, int), int, int, int);  // 第一个参数是双参数函数指针,第二到四个参数为int
fp2 = add2;    // fp2赋值为类型一致的函数名
cout << "1 + 2 + 3 = " << fp2(fp,1,2,3) << endl;

fp2 = add3;    // 与上面等价,add3和add2实际是一样的,都能被fp2接收
cout << "4 + 5 + 6 = " << fp2(fp,4,5,6) << endl;

return 0;

}

输出结果:

1 + 2 = 3
1 + 2 + 3 = 6
4 + 5 + 6 = 15

2、返回值为函数指针

函数指针作为返回值的写法:只需要将int fun(int)变成 int (fun(int))(double, double), 表示fun(int)返回一个函数指针pf(指向类型为int (pf)(double, double))。

int (*createAdd())(int, int); // 定义一个名为createAdd()的函数,输入参数为空,返回值为一个函数指针pTemp(其类型为int (pTemp)(int, int))
int (*createAlgorithm(int type))(int, int); // 定义一个名为createAlgorithm(int)的函数:输入参数为int,返回值为对应运算符的函数指针pTemp(其类型为int (*pTemp)(int, int))

例3:无参数输入的函数createAdd()函数返回一个函数指针:

include

using namespace std;

int add(int x, int y){
return x + y;
}

// 定义一个名为createAdd()的函数
// 输入参数为空
// 返回值为一个函数指针pTemp(其类型为int (pTemp)(int, int))
int (
createAdd())(int, int){
return add;
}

int main(){
int (*fp)(int, int); // 声明一个函数指针fp
fp = createAdd(); // fp接收createAdd()返回值,二者都是同类型的函数指针,匹配
cout << "1 + 2 = " << fp(1,2) << endl;
return 0;
}

输出:

1 + 2 = 3

例4:带参数输入的createAlgorithm(int type)函数返回一个函数指针:

include

using namespace std;

// 定义三种运算:空运算、加法、减法
int none(int , int ){
return 0;
}
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}

// 定义一个名为createAlgorithm(int)的函数:
// 输入参数为int,表示选择哪一种运算符(1为加法,2为减法)
// 返回值为对应运算符的函数指针pTemp(其类型为int (pTemp)(int, int))
int (
createAlgorithm(int type))(int, int){
switch(type){
case 1:
return add;
break;
case 2:
return substract;
break;
default:
return none;
}
}

int main(){
int (*fp)(int, int); // 声明一个函数指针fp
fp = createAlgorithm(1); // fp接收createAlgorithm(1)返回值,二者都是同类型的函数指针,匹配
cout << "1 + 2 = " << fp(1,2) << endl;
fp = createAlgorithm(2); // fp接收createAlgorithm(2)返回值,二者都是同类型的函数指针,匹配
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}

输出:

1 + 2 = 3
5 - 1 = 4

例5:输入参数和返回参数都含有函数指针:(int (calculateAndTransfer(int (fun)(int, int), int x, int y))(int, int){...})

include

using namespace std;

int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}

// 定义一个名为calculateAndTransfer(int)的函数:在内部执行一次计算,并且把输入的函数指针再返回,传递下去
// 输入参数为函数指针(其类型为int (pTemp)(int, int))和两个int变量
// 返回值为同样的函数指针pTemp(其类型为int (
pTemp)(int, int))
int (calculateAndTransfer(int (fun)(int, int), int x, int y))(int, int){
cout << "fun(x,y) = " << fun(x,y) << endl;
return fun;
}

int main(){
int (*fp)(int, int);
fp = calculateAndTransfer(add,1,2);
cout << "1 + 2 = " << fp(1,2) << endl;
fp = calculateAndTransfer(substract,5,1);
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}

输出:

fun(x,y) = 3
1 + 2 = 3
fun(x,y) = 4
5 - 1 = 4

可见,当函数指针出现在输入参数或者返回值位置时,代码可读性将会大大下降,这不利于项目的开发和维护。
3、typedef提高代码可读性
3.1 typedef自定义类型别名

通过上面的例子发现,当函数输入参数或者返回值包含函数指针时,代码可读性将会大大降低。为了解决这个问题,C++中可以使用typedef预先给某一类型起别名,然后再用自定义的别名会写出更让人容易理解的代码。

函数/函数指针使用typedef的两种方式:

typedef int Fun(int, int); // 类型别名:Fun代表一类函数
typedef int (*PFun)(int, int); // 类型别名:PFun代表一类函数指针 (int (*pf)(int, int))

第一种方式的Fun是自定义的函数别名,第二种方式的PFun是自定义的函数指针别名。在作为形参输入时,二种方式等价(编译器会自动把Fun转换为函数指针),但仍然推荐第二种方式,无需隐式转换,让人更容易理解。
3.2 用typedef重写:函数指针作为输入参数

例6、用typedef重写:函数指针作为输入参数:

include

using namespace std;

typedef int Fun(int, int); // 类型别名:Fun代表一类函数
typedef int (*PFun)(int, int); // 类型别名:PFun代表一类函数指针

int add(int x, int y){ // 双参数函数add2:两个数相加
return x + y;
}

int add2(int (*fun)(int, int), int x, int y, int z){ // 利用双参数输入的fun(), 构造三参数函数add2()
return fun(fun(x,y),z);
}

int add4(PFun fun, int x, int y, int z){ // 与add2等价, 利用typedef提高可读性,构造三参数函数add3()
return fun(fun(x,y),z);
}

int add5(Fun fun, int x, int y, int z){ // 与add4等价, 函数名会被自动转换为函数指针作为形参
return fun(fun(x,y),z);
}

int main(){
// 函数指针直接使用:
int (*fp)(int, int);
fp = add;
cout << "1 + 2 = " << fp(1,2) << endl;

// 同上,但使用typedef提高可读性
PFun fp2 = add;  // 与fp是同一类型
cout << "1 + 2 = " << fp2(1,2) << endl;

// 函数指针作为输入参数(形参)时,使用typedef的add4和add5的代码可读性比add2更高:
cout << "1 + 2 + 3 = " << add2(fp2, 1, 2, 3) << endl;
cout << "1 + 2 + 3 = " << add4(fp2, 1, 2, 3) << endl;
cout << "1 + 2 + 3 = " << add5(fp2, 1, 2, 3) << endl;

return 0;

}

3.3 用typedef重写:函数指针作为返回值:

以下两者为等价写法:都是定义一个名为createAlgorithm(int)的函数,输入参数为int,返回值为函数指针pTemp(其类型为int (*pTemp)(int, int))

int (createAlgorithm(int type))(int, int){ ...} // 不使用typedef,返回类型写法难以阅读
typedef int (*PFun)(int, int);
PFun createAlgorithm(int type){ ... } // 可见,用typedef定义的类型PFun写法同一般的返回类型写法,更容易理解

例7:用typedef重写:函数指针作为返回值:

include

using namespace std;

// PFun是一个函数指针的类型别名,指向函数的类型为int (pTemp)(int, int)
typedef int (
PFun)(int, int);

// 定义三种运算:空运算、加法、减法
int none(int , int ){
return 0;
}
int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}

// 定义一个名为createAlgorithm(int)的函数:
// 输入参数为int,表示选择哪一种运算符(1为加法,2为减法)
// 返回值为对应运算符的函数指针pTemp(其类型为int (pTemp)(int, int))
// 等价于:int (
createAlgorithm(int type))(int, int){ xxx }
PFun createAlgorithm(int type){
switch(type){
case 1:
return add;
break;
case 2:
return substract;
break;
default:
return none;
}
}

int main(){
PFun fp = createAlgorithm(1); // fp接收createAlgorithm(1)返回值,二者都是同类型的函数指针,匹配
cout << "1 + 2 = " << fp(1,2) << endl;
fp = createAlgorithm(2); // fp接收createAlgorithm(2)返回值,二者都是同类型的函数指针,匹配
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}

输出:

1 + 2 = 3
5 - 1 = 4

注:类型别名除了typedef之外,还有using和decltype,都可以实现函数指针的别名定义。例如下面三条语句定义的PFun1, PFun2, PFun3等价。

例8:typedef、using和decltype三种等价写法:

include

using namespace std;

int add(int x, int y){
return x + y;
}

// 以下为类型别名的三种等价写法:
typedef int (PFun1)(int, int); // 类型别名:PFun1代表一类函数指针
using PFun2 = int (
)(int, int); // 类型别名:PFun2与PFun1等价
typedef decltype(add) *PFun3; // 类型别名:PFun3与PFun1等价

int main(){
PFun1 fp1 = add;
PFun2 fp2 = add;
PFun3 fp3 = fp2;
cout << "1 + 2 = " << fp1(1,2) << endl;
cout << "1 + 2 = " << fp2(1,2) << endl;
cout << "1 + 2 = " << fp3(1,2) << endl;
return 0;
}

输出:

1 + 2 = 3
1 + 2 = 3
1 + 2 = 3

3.4 用typedef重写:输入参数和返回参数都含有函数指针:以下两种写法等价:

int (*calculateAndTransfer(int (*fun)(int, int), int x, int y))(int, int)
typedef int (*PFun)(int, int);
PFun calculateAndTransfer(PFun fun, int x, int y); // typedef大大提高可读性

例9:用typedef重写例5,让代码具有更好的可读性:

include

using namespace std;

typedef int (*PFun)(int, int);

int add(int x, int y){
return x + y;
}
int substract(int x, int y){
return x - y;
}

// 定义一个名为calculateAndTransfer(int)的函数:在内部执行一次计算,并且把输入的函数指针再返回,传递下去
// 输入参数为函数指针(其类型为int (pTemp)(int, int))和两个int变量
// 返回值为同样的函数指针pTemp(其类型为int (
pTemp)(int, int))
// 下面等价于:int (calculateAndTransfer(int (fun)(int, int), int x, int y))(int, int){
PFun calculateAndTransfer(PFun fun, int x, int y){
cout << "fun(x,y) = " << fun(x,y) << endl;
return add;
}

int main(){
PFun fp;
fp = calculateAndTransfer(add,1,2);
cout << "1 + 2 = " << fp(1,2) << endl;
fp = calculateAndTransfer(substract,5,1);
cout << "5 - 1 = " << fp(5,1) << endl;
return 0;
}

输出:

fun(x,y) = 3
1 + 2 = 3
fun(x,y) = 4
5 - 1 = 6

函数指针的数组

定义一个由函数指针组成的数组:([]的优先级高于,因此直接把(fp)变为(*fp[3])即可

int (*fp[3])(int, int) : fp是一个数组,元素个数为3,每个元素是一个函数指针(指向int (*pf)(int, int)的类型)
int (*(fp[3])(int, int) : 与上面等价,fp是一个数组
int ((*fp)[3])(int, int) : (非法,编译会报错:```error: declaration of 'gp' as array of functions):fp是一个指针,指向一个数组,这个数组的元素个数必须为3,每个元素是一个函数,注意,是函数而不是函数指针,*符号给到fp头上了,因此,编译器会认为你要创建函数的数组,这是不允许的,只允许创建函数指针的数组。

例10:函数指针数组:

include

using namespace std;

typedef int (*PFun)(int, int);

int add(int x, int y){
return x + y;
}

int substract(int x, int y){
return x - y;
}

int main(){
// fp是一个数组,元素个数为2,每个元素是一个函数指针(指向int (pf)(int, int)的类型)
int (
fp[2])(int, int);
fp[0] = add;
fp[1] = substract;
cout << fp0 << endl;
cout << fp1 << endl;

// 下面会报错,从字面上理解:gp是一个指针,指向函数的数组,而函数的数组是非法的,函数指针的数组是允许的
// int ((*gp)[2])(int, int);  \\ error: declaration of 'gp' as array of functions

return 0;

}

输出:

8
2

类的成员函数(方法)的指针

与普通函数指针类似,类的静态/非静态成员函数(方法)指针也有声明、赋值、调用三个步骤:
普通函数 类的非静态成员函数
(类名为A,实例对象为a,对象指针为pa) 类的静态成员函数(类名为A)
函数声明 int fun(double x, double y); int A::fun(double x, double y); int A::static_fun(double, double);
函数指针的声明 int (fp)(double, double); int (A::fp2)(double x, double y); int (fp3)(double, double);
函数指针的赋值 fp = fun; 或 fp = &fun; fp2 = A::fun;或 fp2 = &A::fun; fp3 = A::static_fun;
函数指针的调用 fp(x, y); 或 (
fp)(x, y); (a.fp2)(x,y);
(pa->
fp2)(x,y); fp3(x, y); 或 (*fp3)(x, y);

几个注意点:

新引入三个运算符:声明::*, 调用.*和->*。
声明时,只有类的非静态成员函数指针前才加类名。
赋值时,函数指针前都不加类名,只有类的非静态成员函数指针的等号右侧的才加类名。
调用时,只有类的非成员函数指针前要加对象名,因为必须通过this指针来决定调用的是哪个实例对象的函数。其中(a.*fp2)(x,y)和(pa->*fp2)(x,y)的小括号不能省略,因为.*和->*的优先级低于()。

[box.wed921.com)
[box.zhidianjiaoyu.com)
[box.zgbzcq.com)
[box.myhallo.com)
[box.wuyouliangpin.com)
[box.tupiangc.com)
[box.tongfengstone.com)
[box.bn47.com)

总之,类的静态成员函数与普通函数非常类似,只有在赋值的时候需要等号右侧是类名::静态函数的形式,其他的语法完全一致。而类的非静态成员函数在声明时前面要加类名,赋值时等号右侧要加类名,调用时前面要加具体的对象名。

例11:比较普通函数、类的非静态成员函数、类的静态成员函数的声明、赋值和调用:

include

include

using namespace std;

class Mobile{
public:
Mobile(string number):_number(number){}
void from(string another_number){
cout << _number << " is receiving a call from " << another_number << endl;
}
void to(string another_number){
cout << _number << " is calling to " << another_number << endl;
}
static void printInfo(){
cout << "Static function: Mobile works good!" << endl;
}
private:
string _number;
};

void fun(string number){
cout << "Normal function: number " << number << endl;
}

int main(){
// 普通函数的指针:
void (fp1)(string); // 声明
fp1 = fun; // 赋值
fp1("999"); // 调用
(
fp1)("999"); // 调用的第二种等价方式

// 类的非静态成员函数(方法)的指针:
Mobile m("666888"), *mp = &m;  
void (Mobile::*fp2)(string);   // 声明,使用 ::*  (小括号不能少:写成Mobile::*fp2(string)会报错)
fp2 = Mobile::from;            // 赋值,也可以写成fp2 = &Mobile::from; 因为::优先级高于&
(m.*fp2)("12345");             // 调用,不能写成 m.*fp2("12345") 因为.*的优先级低于()
// (*(m.*fp2))("12345");       // 错误,调用没有像普通函数一样的第二种等价方式(前面加*)
(mp->*fp2)("12345");           // 调用,不能写成 m->*fp2("12345") 因为->*的优先级低于()
fp2 = Mobile::to;              // 赋值 
(m.*fp2)("54321");             // 调用
(mp->*fp2)("54321");           // 调用

// 类的静态成员函数(方法)的指针:
void (*fp3)();                 // 声明: 同普通函数
fp3 = Mobile::printInfo;    // 赋值:等号右侧用类名::静态函数,也可以写成fp3 = &Mobile::printInfo; 
fp3();                      // 调用:同普通函数
(*fp3)();                   // 调用的第二种等价方式:同普通函数

return 0;

}

输出:

Normal function: number 999
Normal function: number 999
666888 is receiving a call from 12345
666888 is receiving a call from 12345
666888 is calling to 54321
666888 is calling to 54321
Static function: Mobile works good!
Static function: Mobile works good!

相关文章
|
24天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
16天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
20天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2577 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
18天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
3天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
2天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
164 2
|
20天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1576 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
22天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
978 14
|
4天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
221 2
|
17天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
735 9