《C++入门篇》——弥补C不足
文章目录
- 前言
- 一.命名空间
- 二.缺省参数
- 三.函数重载
- 四.引用
- 4.1引用做参数
- 4.2引用做返回值
- 五.内联函数
- 六.小语法
- 6.1auto
- 6.2范围for
- 6.3空指针
前言
C++是业内一门久负盛名的计算机语言,从C语言发展起来的它,不仅支持C语言的语法,还新添加了面向对象、泛型等特性,以及祖师爷本贾尼博士补充C语言的不足。
这一篇我们先来讲讲C++对C语言不足部分的补充。
一.命名空间

Cpp能很好的支持C,所以在Cpp文件中写C语言程序是没问题的。
从这段代码里,我们好像看不出什么问题,唯一觉得奇怪的是,为什么整型变量要取名为rand这么奇怪。我们接着往下看:

将变量rand放到全局中,报错信息给出rand重定义,我们想起C语言库里定义有一个叫rand函数与我们定义的rand变量命名冲突了,于是报出了这个语法错误。
那么上面为什么放在局部中没有报错呢?这是因为局部和全局都有时,局部优先!
到这里读者可能会说,在日常写代码中,自己写的又不一定会和自己命名冲突,那么在一个大工程里,数十几个程序员的代码合并到一起,会不会冲突?

使用命名空间,将其隔离起来,这样就不冲突了,打印rand时,找的是库里的rand函数。

使用域作用限定符::,可以让编译器在找rand时,先找域作用限定符指定的域里先去找。
#include <stdio.h>
namespace name
{//可以嵌套使用,如果一个命名空间内也有命名冲突,可以再隔离。namespace name1{int a = 0;}namespace name2{int a = 1;}int Add(int x, int y){return x+y;}struct Node{int val;struct Node* next;};
}int main()
{printf("%d\n", name::name1::a);//到name里找name1,name1里找aprintf("%p\n", name::Add);struct name::Node node = {0};//::要加在Node前面return 0;
}
关于namespace关键字,基本的用法和作用讲完了,还有一个较为重要分文件写声明和定义,接着往下看:
//Stack.h文件
#include <assert.h>
namespace sjr
{typedef struct Stack{int* a;int top;int capacity;}Stack;void StackInit(Stack* ps);void StackPush(Stack* ps, int x);
}//Stack.cpp文件
#include "Stack.h"
namespace sjr
{void StackInit(Stack* ps){assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;}void StackPush(Stack* ps, int x){//...}
}
将两个不同文件的声明和定义使用同名的命名空间包起来,就可以实现声明和定义分离的同时都在命名空间内。
编译器会将不同文件的同名命名空间合并在一起,一个文件里有同名的命名空间也会合并,只是我们一般不会这么写。
总结:命名空间是用来弥补C语言命名冲突的不足。有如何创建命名空间、命名空间里的变量、函数、类型都可以正常创建、可以嵌套、声明和定义分离使用同一个命名空间,编译器会合并它们。
域作用限定符是一种指定命名空间里找的方法,以上面栈为例子,我们试着创建栈并插入几个数据:
#include "Stack.h"/*int main
{name::Stack st;name::StackInit(&st);name::StackPush(&st, 1);name::StackPush(&st, 2);name::StackPush(&st, 3);name::StackPush(&st, 4);//每次使用都需要指定,日常练习这样完全没必要
}*///展开命名空间
using namespace name;
int main
{Stack st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);
}
展开命名空间就可以直接使用里面的变量、函数等,但是这和头文件的展开不同,它只是将隔离拆除了,编译器会到展开的命名空间里面去找。如果展开命名空间里的定义与全局的有命名冲突,这样还是会报错!
在项目中全展开不是很好的做法,还有一种指定展开的方法:
//第一个C++代码:cout是输出,使用流插入<<,自动识别类型;endl是换行 end line;
#include <iostream>using namespace std;//展开std(std是C++官方库的命名空间)
int main()
{ //cout 和 endl都在命名空间里cout << "hello world"<< endl;return 0;
}
#include <iostream>
//为了cout和endl展开整个库太坑了
using std::cout;
using std::endl;
int main()
{int a = 0;cout << "hello world"<< endl;//cin也在std里,没有指定展开,需要加std::std::cin >> a;//cin是输入,使用流提取>>,自动识别类型。 return 0;
}
总结:编译器默认不会到命名空间里找,这是解决命名冲突的基础。使用::可以单次到指定空间里找、还有using namespace std;展开命名空间,此时里面定义的所有东西都将暴露出来、而using std::cout;则只暴露std里面的cout。
二.缺省参数

在形参的后面加上一个值,就是缺省参数。Func函数里的a就是缺省参数,在调用Func函数时,如果没有传参,缺省值10将会默认赋值给a打印,否则按实际传递的值打印。
接下来我们看缺省参数更多的知识:


以上是全缺省的细节,也就是缺省值要从左往右给。

半缺省遵循缺省参数从右往左给。
这是因为,如果不遵循这个规则,对于传参有很大的歧义。比如Fun2(10);,此时这个10是传给a还是传给b是不确定的;不支持跳跃传参Fun2(,10);,总之对于半缺省,遵循以上规则。
缺省有什么用?请看以下代码:
//Stack.h文件
#include <assert.h>
namespace sjr
{typedef struct Stack{int* a;int top;int capacity;}Stack;//声明和定义分离,缺省写在声明里!void StackInit(Stack* ps, int n = 4);void StackPush(Stack* ps, int x);
}//Stack.cpp文件
#include "Stack.h"
namespace sjr
{void StackInit(Stack* ps, int n)//不写缺省,但要写对应类型的形参{assert(ps);ps->a = (int*)malloc(sizeof(int)*n);ps->top = 0;ps->capacity = 0;}void StackPush(Stack* ps, int x){//...}
}//Test.cpp文件
#include "Stack.h"
using namespace sjr;
int main()
{//不知道要多少空间Stack st1;StackInit(&st1);//默认开辟4个整型空间//知道要多少空间Stack st2;StackInit(&st2, 100);//此时我们显示传多少,就开多大//使逻辑清晰且知道需要多大空间时减少扩容消耗。return 0;
}
函数声明、定义分离,缺省参数只写在声明里,不能声明定义同时写缺省,这是为了避免声明和定义缺省值不一。
比如在声明里的缺省值是10,在定义里的缺省值是20这种情况。
为什么是在声明里给缺省参数,而不是定义里给:这是因为有定义的地方一定包括声明,有声明的地方不一定有定义。
比如Test.cpp包含头文件Stack.h,如果声明里没有缺省参数,那么到StackInit这个函数的时候,编译器并不知道有没有缺省参数存在。
总结一句话:函数声明定义分离,缺省参数写在声明里。
三.函数重载
函数重载指的是函数名相同、参数类型不同、个数不同、顺序不同的函数。

同为Add函数名的有两个,从C语言的角度看,main函数里调用的都是同一个函数,这是因为C语言不支持函数重载(仅根据函数名区分函数)。
C++区分函数不仅仅看函数名,还看函数的参数类型、个数、顺序等。C++中以上两个Add函数构成重载,main函数里两个int实参调用int加法,两个double实参调用double加法。
但是如果我们调用Add(1,2.2);时,隐式转换将产生调用歧义(即使构成函数重载,也要注意细节)。接下来我们看缺省参数属于什么类型:

Func(int a = 0)这个函数参数虽然是缺省参数,但是它的类型依旧是int,也就是所它们两个函数不能构成重载,并且传参调用有二义性。
函数构成重载的条件,类型和个数不同都好理解,顺序不同是什么意思呢?

总结:函数重载是C++支持的一种不同于C语言的特性,不同函数的函数名可以相同,但参数的类型、个数、顺序需要有不同的。此外函数返回值不参与构成函数重载的评判(调用时用不到),因为调用时无法根据参数列表确定调用哪个重载函数。
四.引用
引用是给已经存在的变量取一个别名,也就是说,一块空间可以用多个名称表示。

c是a的别名,可以继续给a起别名,也可以给a的别名(c)起别名,都指向同一块空间(a)。并且引用必须初始化,不能更改指向。以上是引用的基本使用方法。
那么引用的作用是什么,如果仅使用这个功能是没有意思的,接下来我们看引用的应用场景:
4.1引用做参数
#include <iostream>void Swap(int* left, int* right)
{int tmp = *left;*left = *right;*right = tmp;
}
//left是a的别名,right是b的别名,在函数里的改变会影响外面的a,b
void Swap(int& left, int& right)//int& left 也是int类型
{int tmp = left;left = right;right = left;
}
int main()
{int a = 10;int b = 20;//函数重载Swap(&a, &b);Swap(a, b);return 0;
}
使用引用做参数,形式看起来比较容易,而且熟悉之后,理解起来也很容易。
我们再讲讲之前单链表需要传递二级指针的理解问题,对比使用引用和指针的区别更进一步体会引用做参数带来形式上的简便。
typedef struct SListNode
{int val;struct SListNode* next;
}SLNode,*PSLNode//简写
void SListPushBack(SLNode** pphead, int x)
{if(*pphead == NULL){*pphead = newnode;//要把plist从空指针改成指向第一个结点}else{//找尾tail->next = newnode;// }
}int main()
{SLNode* plist = NULL;SListPushBack(&plist, 1);SListPushBack(&plist, 2);SListPushBack(&plist, 3);SListPushBack(&plist, 4);
}
在尾插时需要判断plist是否为空,为空则要改变SLNode*类型的变量,假如形参部分写着SLNode* phead,那就只是plist的一份拷贝,phead的改变不会影响plist,所以要传二级指针。
那么学了引用,怎么用引用传参呢?请看下面代码:
typedef struct SListNode
{int val;struct SListNode* next;
}SLNode,*PSLNodevoid SListPushBack(SLNode*& phead, int x)
{if(phead == NULL){phead = newnode;//phead是SLNode*类型 是plist的别名,改变phead就会改变plist}else{//找尾tail->next = newnode;// }
}int main()
{SLNode* plist = NULL;SListPushBack(plist, 1);SListPushBack(plist, 2);SListPushBack(plist, 3);SListPushBack(plist, 4);
}
甚至使用上结点指针类型重名命的PSLNode创建变量,让不是很懂引用的初学者糊涂。
这以上是引用做参数的应用场景,使用指针也可以做到,引用和指针在做参数的时候都可以提高效率,只是指针用起来形式更复杂一点。
接下来说引用做返回值的应用场景:
4.2引用做返回值
int Count()
{int n = 0;n++;return n;
}
int main()
{int ret = Count();return 0;
}
不知道读者有没有思考过,局部变量在出了作用域后生命周期结束。对于上面Count函数里的局部变量n,return n;的时候,Count函数调用结束。
如果返回n的话,那就相当于是访问一个被释放了的空间。其实了解过函数与栈帧的读者知道,n在结束生命周期前拷贝给一个寄存器(由于n变量较小),这个寄存器代替n作为Count的返回值赋值给ret。
也就是说传值返回会进行拷贝,和传值传参一个道理(传值传参会生成一个临时拷贝)。
上述代码使用传值返回是对的,如果使用传引用返回会是怎么样的呢?
int& Count()
{int n = 0;n++;return n; //传引用返回,传n的别名返回
}
int main()
{int ret = Count();//n的别名赋值给ret,相当于把n赋值给ret,因为n的别名指的也是nreturn 0;
}
前面讲过,在return n;的时候,n就被释放掉了。于是返回n赋值给ret的应该是随机值。
如果操作系统还没清理n变量这块空间,那么仍有可能保留着1,否则会被刷成随机值,并且根据不同编译器可能还有所不同。

由于这种随机性,编译器会报出警告。因此返回会销毁的变量时,不采取传引用返回。(注意:会销毁的变量使用引用返回是错误的程序)
接着再进一步看使用引用接收引用返回(会销毁的变量)会如何。

ret是n的别名,相当于ret指向n那块释放了的空间。那么第一次打印取决于n的空间有没有被清理、取决于是什么编译器,因此是随机值。第二次打印的时候,我们看到那块区域已经被清理了。

这里执行了Add(3, 4);后,ret打印出来的值就变成了7。这是由于栈空间复用的原因,同一个函数或结构相似的函数连续调用,上一次的栈帧销毁后,立刻为下一次相同函数调用做准备,局部变量的地址不变。
因此ret指向的z的那块空间被第二次的Add函数调用改成7,但是打印出来的也是随机值,具体取决编译器和操作系统,VS2019打印的是7。
以上都是使用引用返回不恰当场景导致的结果,真正使用引用返回的场景是返回不会销毁的。比如malloc在堆上的对象、静态变量等等。
那么引用做返回值的价值是什么:提高效率和可以修改返回值。指针也可以做到,但形式复杂。当然引用还有指针更适合使用的场景,入门篇先不讲。
int main()
{//权限平移const int a = 10;const int& b = a;//权限缩小int c = 20;const int& d = c;//引用可以是常量const int& e = 10;//权限放大const int f = 5;int& g = f;//errorc = f//c是int类型,f是const int会不会有问题?return 0;
}
引用和指针一样存在权限缩放的问题,权限可以平移、可以缩小,不能放大。
f赋值给c是没问题的,它是值拷贝(不存在权限问题),c和f不属于同一块空间,改变c不影响f。

引用和指针的区别:引用是别名,不开空间,指针存储变量的地址;引用必须初始化,并且不能更改引用对象,指针可以不初始化,也可以更改指向;引用没有空引用,指针有空指针;
五.内联函数
内联函数的关键字是inline,这是用来替代宏函数的。使用内联函数可以使代码量少的函数在调用处展开,避免栈帧创建和销毁的损耗。
宏函数的缺点是:写法复杂(括号较多);宏在预处理阶段就进行替换了,不能调试;没有类型检查;
而使用内联函数避免了宏的缺点,写法就是正常写函数一样,只需在函数返回类型前加inline就变为内联函数,可以进行调试,也有类型检查。

这里即使Add函数很短,调试进入反汇编还是选择调用,而不是像宏一样展开的原因是,Debug版本下默认内联函数是不会展开的,要把属性修改一下:


设置完成后,我们再调试起来看看效果:

虽说是展开,但不是把Add函数里的代码全部放到调用处,而是编译器实现和函数逻辑一样的指令。
注意内联函数在调用处选不选择展开取决于编译器,不是加了inline关键词的函数就会展开,编译器只会展开代码量少的函数。
这是因为如果有程序员给长代码函数、循环函数、递归函数加上内联,并且编译器无条件展开则会导致需要执行的指令变得非常多,生成的可执行程序文件特别大。
内联函数还有一个特别的点:
//Func.h
#include <iostream>
using namespace std;inline void Func(int a = 10);//Func.cpp
#include "Func.h"void Func(int a)
{cout << a << endl;
}//Test.cpp
#include "Func.h"int main()
{Func();return 0;
}

出现了链接错误,这是因为内联函数在编译时,Func.cpp文件包含头文件得知Func函数是内联函数后,就没有把函数的地址放进符号表,因为在链接的时候,Test.cpp文件找不到Func函数的地址。
所以当内联函数声明和定义分离时,使用只能在定义的那个文件里使用。
正确的使用内联函数的方法是,将内联函数完整的实现放在头文件中,这样函数就可以在调用的地方直接展开,而不用在链接时候找地址。
六.小语法
6.1auto
auto是C++一个用来自动推导类型的关键字,它的用途是对长类型的省略写法。

对于指针的写法可以写auto* d = c;,而对于引用我们只能显示写,以上只是说明atuo的用法,对于这种短类型,不是auto的真正使用场景。
注意:auto不能做参数类型,也不能做函数返回类型以及不能用来创建数组。
6.2范围for
对于数组的遍历,C语言使用求数组下标依次遍历,C++使用一个更为简便的语法:

for(auto e: 数组)这个语法中e是一个和数组元素类型一样的临时变量,将数组里的值依次取出赋值给e,自动判断结束。习惯使用auto当e的类型,让其自动推导类型。
当e作为数组里每个元素的别名时,对其进行修改会影响数组里的元素。
由于冒号后面加的是数组,因此:
#include <iostream>void Func(int arr[])
{for(auto e : arr)//error,arr是首元素地址不是数组{//...}
}
int main()
{int arr[3] = {1,2,3};Func(arr);return 0;
}
6.3空指针
在C++程序中,使用nullptr当做空指针,C语言的NULL有点错误,因此C++委员会后来补上这个坑,引入nullptr这个关键词,实质是void*。

好了,以上就是C++入门篇,希望读者有所收获!
相关文章:
《C++入门篇》——弥补C不足
文章目录 前言一.命名空间二.缺省参数三.函数重载四.引用4.1引用做参数4.2引用做返回值 五.内联函数六.小语法6.1auto6.2范围for6.3空指针 前言 C是业内一门久负盛名的计算机语言,从C语言发展起来的它,不仅支持C语言的语法,还新添加了面向对…...
要在Linux上安装Docker Compose和nginx
一、要在Linux上安装Docker Compose,您可以按照以下步骤进行操作: 确保您的Linux系统已经安装了Docker。您可以通过运行以下命令来检查Docker是否已经安装: docker --version如果Docker未安装,请先安装Docker。 下载Docker Compo…...
zsh插件之gitignore安装使用教程
安装 zsh 插件管理工具 首先,确保你已经安装了 zsh,然后安装 Oh My Zsh,这是一个流行的 zsh 配置框架。在终端运行以下命令安装 Oh My Zsh: bashCopy code sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/ma…...
十二、Qt 操作PDF文件(2)
一、在《十、Qt 操作PDF文件-CSDN博客》中我们用Poppler类库打开了PDF文件,并显示到窗体上,但只能显示一页,功能还没完善,在本章节中,加入了: 通过选择框选择PDF文件并打开,默认打开第一页。通…...
Flutter系列:Flutter常见问答(可用于面试)
Flutter系列 Flutter常见问答 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/135604801 【简介】&#…...
聚合收益协议 InsFi :打开铭文赛道全新叙事的旋转门
“InsFi 协议构建了一套以铭文资产为基础的聚合收益体系,该体系正在为铭文资产捕获流动性、释放价值提供基础,该生态也正在成为铭文赛道掘金的新热土。” 在 2023 年年初,Ordinals 协议在比特币链上被推出后,为比特币链上带来了…...
【信号与系统】【北京航空航天大学】实验三、连续时间信号的频域分析 【MATLAB】
一、实验目的 1、掌握 傅立叶变换(The Fourier Transform) 及其性质; 2、掌握连续时间信号傅立叶变换的数值计算方法; 3、掌握利用 MATLAB 实现信号的幅度调制(Amplitude Modulation, AM) 的方法ÿ…...
FFmpeg之AVFilter
文章目录 一、概述二、重要结构体2.1、AVFilterGraph2.2、AVFilter2.3、AVFilterContext 三、流程梳理3.1、FFmpeg AVFilter 使用整体流程3.2、过滤器构建流程3.2.1、分配AVFilterGraph3.2.2、创建过滤器源3.2.3、创建接收过滤器3.2.4、生成源和接收过滤器的输入输出3.2.5、通过…...
ES 之索引和文档
本文主要介绍ES中的数据组成结构单元。 一、文档(Document) 1、概念 ES的数据存储单元是面向文档的,文档是所有数据存储,搜索的最小单元。 你可以把ES中的文档对应成mysql中的一条条数据记录。到时候你存进ES的数据就是一个个文档。 文档存入ES是序列…...
使用muduo库编写网络server端
muduo库源码编译安装和环境搭建 C muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装-CSDN博客 #include<iostream> #include<muduo/net/TcpServer.h> #include<muduo/net/EventLoop.h> using namespace std; using namespace muduo; using name…...
Unity3D和three.js的比较
一、Unity3D和three.js简介 Unity3D是一款跨平台的游戏引擎,可以用于开发2D和3D游戏。它提供了一个可视化的开发环境,包含了强大的编辑器和工具,使开发者可以方便地创建游戏场景、添加物体、设置物理效果、编写脚本等。Unity3D支持多种平台,包括PC、移动设备、主机等,可以…...
JavaScript 类型判断及类型转换规则
文章目录 JavaScript 类型及其判断使用 typeof 判断类型使用 instanceof 判断类型使用 constructor 和 Object.prototype.toString 判断类型JavaScript 类型及其转换JavaScript 函数参数传递cannot read property of undefined 问题解决方案分析一道网红题目JavaScript 类型判断…...
ubuntu禁用/启用图形界面
当安装了带图形界的ubuntu的时候,如果觉得图形界面占资源,就需要将图形界面关闭,关闭的方法如下: 1、 打开 /etc/default/grub,修改或增加如下参数: GRUB_CMDLINE_LINUX_DEFAULT"text" GRUB_TE…...
【LeetCode】28. 找出字符串中第一个匹配项的下标(简单)——代码随想录算法训练营Day09
题目链接:28. 找出字符串中第一个匹配项的下标 题目描述 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分ÿ…...
架设一台NFS服务器
1、开放/nfs/shared目录,供所有用户查询资料 2、开放/nfs/upload目录,为192.168.xxx.0/24网段主机可以上传目录, 并将所有用户及所属的组映射为nfs-upload,其UID和GID均为210 3、将/home/tom目录仅共享给192.168.xxx.xxx这台主机…...
MySQL中根据出生日期计算年龄
创建student表 mysql> create table student( -> sid int primary key comment 学生号, -> sname varchar(20) comm…...
ABAP IDOC 2 XML
有个需求,外围系统希望我们给到一个IDOC 记录的样例,但是我们we02中并无法看到 就找了一个demo去直接展示IDOC内容 *&---------------------------------------------------------------------* *& Report Z_IDOC_TO_XML *&------------…...
什么是小程序?特点和技术架构详解
小程序是一种新的移动应用程序格式,一种结合了 Web 技术以及客户端技术的混合解决方案。 传统的原生应用运行起来比较流畅,但是也有天然的基因缺陷: 不支持动态化,发布周期长需要开发Android和iOS两套代码,开发成本高…...
边缘计算的挑战和机遇——数据安全与隐私保护
边缘计算的挑战和机遇 边缘计算面临着数据安全与隐私保护、网络稳定性等挑战,但同时也带来了更强的实时性和本地处理能力,为企业降低了成本和压力,提高了数据处理效率。因此,边缘计算既带来了挑战也带来了机遇,需要我…...
linux-等保三级脚本(1)
该脚本主要是针对 CentOS Linux 7 合规基线加固的一些配置操作,包括创建用户、安全审计配置、入侵防范配置、访问控制配置、身份鉴别策略配置等。如果您需要在脚本中添加公司网址,您可以在适当的位置添加相应的内容。不过请注意,在实际生产环…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
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 开发者设计的强大库ÿ…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
