《C++入门篇》——弥补C不足

news2024/11/23 18:49:28

文章目录

    • 前言
      • 一.命名空间
      • 二.缺省参数
      • 三.函数重载
      • 四.引用
        • 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里找a
    printf("%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,*PSLNode


void 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的别名指的也是n
	return 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;//error

	c = 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++入门篇,希望读者有所收获!

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

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

相关文章

分享用is_sorted()解决单调数列问题

题目名称 896. 单调数列 目录 题目名称 896. 单调数列 1.题目 2.题目分析 3.题目知识 3.1 is_sorted() 3.2.迭代器与反向迭代器 3.2.1理解迭代器 3.2.2正向迭代器 3.2.3反向迭代器 最后&#x1f368; 推荐阅读顺序: 1.题目->2.题目分析->3.题目知识点 1.题目 如…

基于R语言的NDVI的Sen-MK趋势检验

本实验拟分析艾比湖地区2010年至2020年间的NDVI数据&#xff0c;数据从MODIS遥感影像中提取的NDVI值&#xff0c;在GEE遥感云平台上将影像数据下载下来。代码如下&#xff1a; import ee import geemap geemap.set_proxy(port7890)# 设置全局网络代理 Map geemap.Map()# 指定…

安卓屏幕自动息屏时亮度突然变亮

自然息屏流程 USER_ACTIVITY_SCREEN_BRIGHT&#xff08;亮屏&#xff09; → USER_ACTIVITY_SCREEN_DIM&#xff08;DIM&#xff09; → USER_ACTIVITY_SCREEN_DREAM&#xff08;灭屏&#xff09;变化&#xff0c;最终进入ASLEEP后。在息屏时会执行一个变暗的动画 frameworks\…

echarts tooltip显示不全问题

在项目里面的tooltip数据特别多&#xff0c;显示不全问题&#xff1a; 1、如果盒子还够大&#xff0c;只是tooltip飘到上面或者下面被覆盖住了&#xff0c;可以考虑confine: true这个属性&#xff0c;将tooltip限制在盒子内; 2、如果盒子比较小&#xff0c;展示不全的话&#…

【PyTorch】PyTorch之Tensors索引切片篇

文章目录 前言一、ARGWHERE二、CAT、CONCAT、CONCATENATE三、CHUNK四、GATHER五、MOVEDIM和MOVEAXIS六、PERMUTE七、RESHAPE八、SELECT九、SPLIT十、SQUEEZE十一、T十二、TAKE十三、TILE十四、TRANSPOSE十五、UNBIND十六、UNSQUEEZE十七、WHERE 前言 介绍常用的PyTorch之Tenso…

如何使用Synology Drive作为文件同步服务器实现云同步Obsidian笔记

文章目录 一、简介软件特色演示&#xff1a; 二、使用免费群晖虚拟机搭建群晖Synology Drive服务&#xff0c;实现局域网同步1 安装并设置Synology Drive套件2 局域网内同步文件测试 三、内网穿透群晖Synology Drive&#xff0c;实现异地多端同步Windows 安装 Cpolar步骤&#…

Oracle 实战手册 工作实战经验总结

目录 一、基本的数据库管理 1、数据库的启动和关闭 ​编辑2、如何确定Oracle的版本&#xff1f; 3、如何修改数据库的内存参数 4、修改用户名密码 5、如何查看最大会话数 6、如何修改oracle数据库的用户连接数 7、解锁用户 8、如何获取被锁定的数据库对象 9、如何确定…

金融CRM系统是什么?有哪些功能和作用

今年市场经济下行&#xff0c;投资趋向于保守、人们消费降级&#xff0c;对于金融行业来说影响很大。受经济形式的影响加上行业的数字化转型升级&#xff0c;金融企业都在寻求客户管理的新策略&#xff0c;维护好忠实客户、吸引新客户投资。小编认为CRM系统是管理客户的不二之选…

计算机网络——运输层(1)暨小程送书

计算机网络——运输层&#xff08;1&#xff09;暨小程送书 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 运输层概述两个主要协议运输层和网络层的关系网络层运输层总结 多路复用与多路分解多路复用多路分解不同的技术实现时分复用&#xff08;TDM&#xff09;频分复…

maven环境搭建(打包项目)

Maven:直观来讲就是打包写好的代码封装 Apahche 软件基金会&#xff08;非营业的组织&#xff0c;把一些开源软件维护管理起来&#xff09; maven apahce的一个开宇拿项目&#xff0c;是一个优秀的项目构建&#xff08;管理工具&#xff09; maven 管理项目的jar 以及jar与j…

Netty通信中的粘包半包问题(四)

前面我们介绍了特殊分隔符、以及固定长度&#xff0c;今天来介绍一下换行符分割&#xff0c;这种换行符是兼容了Windows和Linux的转义的&#xff0c;前提你的报文中没有换行符或者对换行符做特殊处理 System.getProperty("line.separator")1.Server package splici…

使用 Categraf 采集 Nginx 指标

1. 前言 工作中需要监控 Nginx 的指标&#xff0c;选用的指标采集器是 Categraf&#xff0c;特此记录下&#xff0c;以备后用。 此文档并未详细记录详细的操作细节&#xff0c;只记录了大概的操作步骤&#xff0c;仅供参考。 2. 采集基础指标 2.1. 暴露 Nginx 自带的指标采…

「2023 | 快手」PEPNet:融合个性化先验信息的多场景多任务网络

之前梳理过多场景建模方法&#xff1a;推荐系统(二十四&#xff09;「知识梳理」多场景建模梳理&#xff0c;现在介绍快手提出的多场景多任务方法PEPNet。 Title: PEPNet: Parameter and Embedding Personalized Network for Infusing with Personalized Prior Information F…

GMT学习记录

我主要根据GMT中文手册一步一步学习的&#xff01;&#xff01;&#xff01;&#xff01;B站视频介绍的是5.0老版本仅仅建立基础理解这个软件。 好的&#xff0c;学了一点发现直接把gmt转为shp&#xff0c;就得到我想的文件 gmt数据转shape格式数据 - 简书 (jianshu.com) 命…

OpenEL GS之深入解析视频图像处理中怎么实现错帧同步

一、什么是错帧同步? 现在移动设备的系统相机的最高帧率在 120 FPS 左右,当帧率低于 20 FPS 时,用户可以明显感觉到相机画面卡顿和延迟。我们在做相机预览和视频流处理时,对每帧图像处理时间过长(超过 30 ms)就很容易造成画面卡顿,这个场景就需要用到错帧同步方法去提升…

从前端角度浅谈性能 | 京东物流技术团队(转载)

1 前言 自网站诞生以来&#xff0c;页面白屏时间、用户交互的响应速度等一直都是开发者关心的问题&#xff0c;这直接影响了一个网站能否为用户的浏览提供舒适的服务&#xff0c;而这种舒适度&#xff0c;直接关系着对用户的吸引力&#xff0c;毕竟谁都不能忍受一个页面长达10秒…

HarmonyOS—声明式UI描述

ArkTS以声明方式组合和扩展组件来描述应用程序的UI&#xff0c;同时还提供了基本的属性、事件和子组件配置方法&#xff0c;帮助开发者实现应用交互逻辑。 创建组件 根据组件构造方法的不同&#xff0c;创建组件包含有参数和无参数两种方式。 说明 创建组件时不需要new运算…

What is `addArgumentResolvers` does in `WebMvcConfigurer` ?

addArgumentResolvers 在SpringMVC框架中&#xff0c;主要用于向Spring容器注册自定义的参数解析器。在处理HTTP请求时&#xff0c;SpringMVC会使用这些参数解析器将请求中的数据&#xff08;如查询参数、路径变量、表单数据等&#xff09;转换并注入到控制器方法的参数中。 使…

[C++] VS 2022演练 - 创建和使用静态连接库(Static Lib) (详细图文)

什么是静态连接库? 静态连接库是一种将代码编译成二进制可执行文件时使用的库。在静态链接库中,代码被直接嵌入到可执行文件中,而不是作为外部库单独链接。这意味着当程序运行时,不需要额外的依赖项或库文件。 使用静态连接库的优点是减少了对外部依赖的需求,并且可以更…

【Linux系统编程】环境变量的组织方式

environ和getenv函数 在Linux中&#xff0c;environ是一个全局的外部变量&#xff0c;其类型为char**&#xff0c;存储着系统的环境变量。除了使用主函数中的第三个参数外&#xff0c;我们也可使用environ函数直接访问系统的环境变量。 注意&#xff1a;这里在代码内部使用envi…