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

手撕C++入门基础


1.C++介绍

C++课程包括:C++语法、STL、高阶数据结构

C++参考文档:Reference - C++ Reference

C++ 参考手册 - cppreference.com

cppreference.com

C++兼容之前学习的C语言

2.C++的第一个程序

打印hello world

#define _CRT_SECURE_NO_WARNINGS 1
// test.cpp
// 这⾥的std cout等我们都看不懂,没关系,下⾯我们会依次讲解
#include<iostream>
using namespace std;int main()
{cout << "hello world\n" << endl;return 0;
}

在这个代码中我们有很多地方不清楚

  1. 头文件 #include

  2. main函数上面的代码:using namespace std;

  3. 打印hello world的代码:cout << "hello world\n" << endl;

那么下面我们就逐次进行学习这些我们不懂的东西

3.命名空间namespace

1.namespace的价值

在C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

c语⾔项⽬类似下⾯程序这样的命名冲突是普遍存在的问题,C++引⼊namespace就是为了更好的解决这样的问题

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”printf("%d\n", rand);return 0;
}
//当头文件只有stdio.h的时候是没有问题的,但是我们加上另一个头文件stdlib.h就会出现报错的现象
//在stdlib这个库里面定义了一个rand 的函数,那么我们现在又在外面定义一个rand的变量,那么就会出现冲突

namespace就是命名空间

那么命名空间是什么样子的呢?

2.namespace的定义

• 定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中

即为命名空间的成员。命名空间中可以定义变量/函数/类型等。就不会冲突了

将我们要定义的函数,结构体,全局变量都能放到这个空间中,就不会出现冲突的现象了

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
namespace bit
{// 命名空间中可以定义变量/函数/类型int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}
int main()
{// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”printf("%d\n", rand);return 0;
}
//我们在命名空间中命名全局变量或者函数的话就不会出现冲突了

• namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下⾯的rand不在冲突了。

我们上面的代码一开始加上stdlib会报错的原因是因为stdlib这个头文件展开会有一个叫rand的函数,那么就会于我们主函数中的变量rand起冲突,这两个rand在同一个域中,一定会有问题的

但是现在我们将rand这个全局变量加在了命名空间中,那么这个rand和stdlib的rand函数就在不同的域中,就不会互相影响了

通过域作用限定符 :: 访问域中的全局变量:

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
namespace bit
{// 命名空间中可以定义变量/函数/类型int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}
//int main()
//{
//    printf("%d\n", rand);
//    return 0;
//}
//我们在命名空间中命名全局变量或者函数的话就不会出现冲突了
/*
但是我们现在打印这个rand显示的是这个rand是函数指针,
那么就说明这个rand是这个stdlib头文件展开的里面的rand函数那么我们应该如何将我们命名空间中的rand的值打印出来呢?如果我们想访问这个命名空间内的rand,那么我们需要指定一个这个空间的域
*/
int main()
{printf("%p\n", rand);//00007FFB89514D50//::域作用限定符printf("%d\n", bit::rand);//10//那么这里就说明我们指定要访问bit这个命名空间域里面的randbit::Add(1, 2);struct bit::Node node;   //我们在域作用限定符来创建结构体的时候//我们需要将这个操作符放到结构体类型的名称的前面return 0;
}
/*
那么第一个打印的就是我们stdlib中展开的函数rand的地址第二个打印的就是我们bit这个域中的rand的大小
*/

::域作用限定符 是两个冒号,通过这个符号我们能访问我们在命名空间中命名的量

我们在域作用限定符来创建结构体的时候

我们需要将这个操作符放到结构体类型的名称的前面

不同的域我们是能定义同名的,但是同一个域是不能定义同名的

• C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/

类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响

编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期。

上面命名空间bit里面的rand是局部的还是全局的呢?

是全局的

因为这个命名空间并不影响这些变量的声明周期

我们只是用这个域进行了一个隔离,那就不怕和库里面冲突了

不同类型的域的访问:

int x = 0;//全局域namespace bit
{int x = 1;//命名空间域
}void func()
{int x = 2;//局部域
}
int main()
{int x = 3;//局部域printf("%d\n", x);//这里默认访问的是局部的这个x,因为搜索是先局部再全局搜索,不会到命名空间域和函数域中进行搜索的printf("%d\n", bit::x);//指定一下命名空间就行了printf("%d\n", ::x);//这么就是访问全局变量,左边是空的return 0;
}
//局部域是会影响生命周期的,出了作用域就会被销毁了//我们是不能访问func这个函数内的局部域的,局部域只能在当前这个域内进行访问的//那么我们可以访问自己局部的,可以访问全局的,可以访问命名空间内的//命名空间主要是与全局进行隔离,因为局部域一开始就是被隔离的

命名空间域:bit::x

全局域:::x

局部域:x

• namespace只能定义在全局,当然他还可以嵌套定义。

命名空间只能定义在全局中,不能定义在局部域之中

命名空间是能进行嵌套的,但是嵌套的主要是什么问题呢?怎么嵌套呢?

我们在一个大的类别的命名域中嵌套放着两个多个命名域

我们在bit这个命名空间里面能嵌套一个航哥的命名空间以及鹏哥的命名空间,各自进行隔离,各玩各的,就不存在冲突的问题了

命名空间的嵌套以及空间内的嵌套的访问

// 命名空间可以嵌套
namespace bit
{// 鹏哥namespace pg{int rand = 1;int Add(int left, int right){return left + right;}}// 杭哥namespace hg{int rand = 2;int Add(int left, int right){return (left + right) * 10;}}
}int main()
{printf("%d\n", bit::pg::rand);printf("%d\n", bit::hg::rand);printf("%d\n", bit::pg::Add(1, 2));printf("%d\n", bit::hg::Add(1, 2));return 0;
}

• 项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突。

就相当于合并在一起了

• C++标准库都放在⼀个叫std(standard)的命名空间中。

域做到了名字的隔离,同一个域不能定义同一个变量,不同的域可以定义同一个变量

3.命名空间使⽤

编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。所以下⾯程序会编译报错。所以我们要使⽤命名空间中定义的变量/函数,有三种⽅式:

• 指定命名空间访问,项⽬中推荐这种⽅式。

bit::a

• using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。

• 展开命名空间中全部成员,项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便推荐使⽤。

我们在使用命名空间中的变量或者函数总是要写bit:x

就很复杂,那么我们可以在前面加上using namespace bit

正常的话我们是需要这样写才能访问命名空间内的变量或者函数

我们这个正常的搜索是局部再到全局

加上这个我们就能搜索命名空间内的变量或者函数了

namespace bit
{int a = 0;int b = 10;
}//using namespace bit;//暴露这个命名空间内的所有东西using bit::a;//将指定的变量展露出来,相当于这里将a暴露到全局出来了
int b = 2;int main()
{printf("%d\n", a);printf("%d\n", a);printf("%d\n", a);printf("%d\n", a);printf("%d\n", a);printf("%d\n", a);bit::b++;printf("%d\n", bit::b);//11   命名空间里面的printf("%d\n", b);//2   全局的return 0;
}
/*
在这里我们将a暴露出来,b就正常的方法进行使用,b不暴露*/

可以选择暴露命名空间内的所有东西,也能暴露某一个东西

4.C++输入&输出

• 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输

出对象。

io流

• std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输

⼊流。

• std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。

• std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。

• >是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)

• 使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊输出可以⾃动识别变量类型(本质是通过函数重载实现的,这个以后会讲到),其实最重要的C++的流能更好的⽀持⾃定义类型对象的输⼊输出。

• IO流涉及类和对象,运算符重载、继承等很多⾯向对象的知识,这些知识我们还没有讲解,所以这

⾥我们只能简单认识⼀下C++ IO流的⽤法,后⾯我们会有专⻔的⼀个章节来细节IO流库。

• cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使⽤⽅式去⽤他们。

基本的使用

using namespace std;//加上这个我们下面就可以不用指定了
//就是下面的输入和输出的话我们是不用在前面加std::的
using std::cout;//仅仅值展开一些
using std::endl;int main()
{//  <<这个运算符叫做流插入---输出//std::cout << "hello world\n";//可以输出任意类型的变量和对象cout << "hello world\n";//可以输出任意类型的变量和对象//将这个字符串流向控制台---输出int i = 10;cout << i<<'\n'<<"\n";//支持连续的流插入的    换行就这么写double d = 1.1;cout << d << endl;//  std::cout << d << std::endl;//右边的对象流向左边的控制台对象里面//输入//std::cin >> i >> d;//输入i  再接着输入dcin >> i >> d;//输入i  再接着输入dcout << i << ' ' << d << std::endl;  //输出i  再接着输出d  再输出换行return 0;
}
/*
我们这里是不需要像c语言一样要在前面定义%s %d  这些东西
*/
/*
换行的两种方法:在我们输出的后面接着输出换行'\n'或者是"\n"另一种方法就是在后面输出std::endl
endl就是end line的缩写  结束一行endl这个实际上是一个函数
std::endl这个的兼容性相较于'\n'的话更强一些
根据平台的不同会进行处理的 
*//*
输入输出的话我们用c语言的scanf和printf都是可以的
但是我们要指定类型c++的输入和输出就不用指定类型了C/C++是可以混在一起用的,不会有什么问题的如果想控制打印出来的小数点的精度的话,建议使用printf
因为C++的话控制这个精度的话会很麻烦的
*/

总结:

std::out<<i<<'\n'

输出i且输出换行

out<<i<<'\n'

输出i且输出换行 前提是前面加上using namespace 或者是using std::cout 将这个展开

cin >> i >> d;

输入i再输入d

前提是前面加上using namespace 或者是using std::cin

将这个展开

cout << i << ' ' << d << std::endl;

输出i和输出d再输出换行

换行就是std::endl

如果前面加上了using namespace 或者是using std::endl

那么我们就这么写:cout << i << ' ' << d << endl;

那么这么写就很方便了

#include<iostream>
using namespace std;
int main()
{
// 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码
// 可以提⾼C++IO效率
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
return 0;
}

对于这些输入输出的话我们不是直接操作台,我们的数据都要经历缓冲区

这样就会使效率降低

那么我们加上这个主函数里面的代码就能提高效率了

将这三串代码放在前面,后面的输出输出的话效率会高一些

在io需求比较高的地方,比如大量输入的竞赛题中,加上这三行代码

就能提高C++ IO效率

• ⼀般⽇常练习中我们可以using namespace std,实际项⽬开发中不建议using namespace std。

一般的练习的话我们是将std进行展开的,项目开发是不建议展开的

避免我们的创建的变量和库中的变量冲突了

• 这⾥我们没有包含,也可以使⽤printf和scanf,在包含间接包含了。vs系列编译器是这样的,其他编译器可能会报错。

学到这里我们来回顾一开始打印hell world的代码,这么我们就很清楚每一步的作用了

5.缺省参数

c语言是没有缺省参数的概念

C++支持缺省参数,有些地方将缺省参数叫做默认参数

• 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有些地⽅把缺省参数也叫默认参数)

• 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

• 带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。

• 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。

缺省参数的简单介绍:

#include <iostream>
#include <assert.h>
using namespace std;
void Func(int a = 0)
{cout << a << endl;
}
int main()
{Func(); // 没有传参时,使⽤参数的默认值Func(10); // 传参时,使⽤指定的实参return 0;
}
//如果我们没有传参的话,我们就使用的是这个缺省值
//对于这个函数func的话,我们没有传参的话,那么我们就用函数内的缺省参数作为默认值
//如果传了参的话,我们就用传的参//那么第一个函数打印的就是0
//第二个函数打印的就是10

缺省函数分为全缺省和半缺省

全缺省就是在函数部分我们直接在括号中将所有的变量都进行定定义大小 void Func1(int a = 10, int b = 20, int c = 30)

半缺省的话就是存在没有定义大小的变量 ,就是缺省部分参数

void Func2(int a, int b = 10, int c = 20)

我们是不支持不连续的传参

比如:Func(,2,);

这种写法就是错的

只要缺省一部分那么就是半缺省

我们是不能第一个参数不缺省,第二个参数缺省,第三个参数不缺省

既然缺省了就要连续缺省

缺省函数以及半缺省函数的介绍用法

#include <iostream>
using namespace std;
// 全缺省   缺省全部参数
void Func1(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
// 半缺省  缺省部分参数
void Func2(int a, int b = 10, int c = 20)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
int main()
{Func1();//可以一个都不传,全用的就是缺省值Func1(1);//仅仅传第一个给a,剩下的b和c用缺省值Func1(1, 2);//传a和b,c用缺省值Func1(1, 2, 3);//三个都传,那么三个都不用缺省值//Func1(,2,);//这样写是错的,因为我们这么写不支持,因为我们传参要连续传参Func2(100);Func2(100, 200);    Func2(100, 200, 300);return 0;
}
/*
a = 10
b = 20
c = 30a = 1
b = 20
c = 30a = 1
b = 2
c = 30a = 1
b = 2
c = 3a = 100
b = 10
c = 20a = 100
b = 200
c = 20a = 100
b = 200
c = 300*/

缺省参数的实际应用,我们在创建栈的时候我们需要手动去调节栈的大小,我们就可以设置缺省值,给这个栈设置一个初始的大小,啥都不用去传

如果我们提前知道要开辟多大的空间我们直接给,避免扩容的时候做出的消耗

如果在函数的声明中给了缺省函数的话我们就没必要再定义中再给缺省函数了

避免给的值不一样,就算一样也会报错的

如果函数没有声明的话我们是可以直接在定义中给上缺省函数的

做人不能做缺省参数,就是备胎舔狗

6.函数重载

C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。

同一个作用域中的两个同名参数,总得有点不同

形参不同,参数个数不同,类型不同

函数重载的举例

using namespace std;
//参数类型不同
int Add(int left, int right)//对整数的加法
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)//对小数的加法
{cout << "double Add(double left, double right)" << endl;return left + right;
}// 2、参数个数不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}// 3、参数类型顺序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}//语法上下面的两个函数构成重载,但是我们在调用的时候就会有歧义了
//我们不知道调用的是哪个
//语法上是可以存在的,但是在实际运用中是不能存在的
void f1()
{cout << "f()" << endl;
}
void f1(int a = 10)
{cout << "f(int a)" << endl;
}int main()
{//下面的两个函数其实是不同的函数//参数的类型不同Add(10, 20);Add(10.1, 20.2);//参数个数不同f();f(10);//参数顺序不同  本质还是类型不同f(10, 'a');f('a', 10);return 0;
}
/*
如果是返回值不同那么能不能构成重载呢?
答案是不能的,因为光凭借这个我们是不能进行判断的但是我们的参数个数以及参数顺序和参数类型不同都能进行判断函数的不同,那么就能进行重载
*/

参数不同、参数的类型不同、参数的个数不同

随便满足一个条件的话都构成函数重载

那么我们在调用的时候就不会担心调用的时候调用错了

7.引用

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间。

引用就是取别名

引用的基本用法:

using namespace std;void Swap(int& rx, int& ry)//之前我们是没有&这个稍微,那么这里的参数就是a和b的拷贝
//因为是传值所以是不能进行根本性的交换的
//所以我们在进行交换函数的时候我们是要传地址的,使用指针的//那么现在我们在函数的定义中我们直接将a的别名和b的别名定义在里面
//那么因为rx是a的别名,ry是b的别名,那么我们通过这两个别名就能访问他们的地址了
//并且我们在这个主函数里面我们在进行交换函数的时候我们是不需要进行取地址的操作的
//因为我们的函数传的参是a  那么我们为a取别名叫rx  那么rx就是a//那么rx和ry的交换就是a和e的交换//那么就是形参的改变就改变了实参
{int tmp = rx;rx = ry;ry = tmp;
}
int main()
{int a = 0;//int b = a;//将a赋值给bint& b = a;//给a的这块空间取别名。叫bint& c = a;//再给a取别名叫cint& d = b;//也可以给b取别名//其实就是这么一块空间有多个名字cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;//这四个变量的地址都是一样的b++;//那么我们进行b++的话,a c d 的大小都会被改变的//我们还能给指针p1取别名int* p1 = &a;int*& p2 = p1;int e = 10;p2 = &e;//将p2改变指向从a变成e //因为p1就是p2//那么随着p2的改变,那么p1也改变了cout << *p1 << endl;int x = 0, y = 1;cout << x << " " << y << endl;Swap(x, y);cout << x << " " << y << endl;return 0;return 0;
}//我们这里是给变量取别名
//但是typedef是给类型取别名
//typedef int type  
// 
//define是定义宏//&这个在c语言中是取地址的符号,但是我们在这里是引用的意思
//就是给这个变量起个小名//引用是不能替代指针的

引用等价于指针的利用:

在函数传参的时候我们使用引用我们既不用传地址了

typedef struct ListNode
{int val;struct ListNode* next;
}LTNode, * PNode;
// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名
// 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PNode& phead, int x)
{PNode newnode = (PNode)malloc(sizeof(LTNode));newnode->val = x;newnode->next = NULL;if (phead == NULL){phead = newnode;}else{//...}
}int main()
{PNode plist = NULL;ListPushBack(plist, 1);   //那么我们传上去的plist的别名就是pheadreturn 0;
}
//将plist引用一个新的名字

引用就是给我们的变量换个名字

这些名字都指向了这个变量,地址都是相同的

引用就是取别名

我叫XX凯

朋友叫我凯子

这两个名字都指向的是我本人

这就是引用

一个变量是可以有多个名字的

引用的特点就是在新名字前面加上&这个符号

类型&引用别名=引用对象

在定义变量,在类型后面加&这个就是引用,其他的位置都是取地址的意思

我们给一个变量取了别名,我们还能给别名取别名,最后这些名字都指向最开始的变量

引用的特性

• 引⽤在定义时必须初始化

int&c;

这种写法就是错的,没有进行初始化

• ⼀个变量可以有多个引⽤

一个变量可以起很多个别名

• 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体

就是我现在是你的别名,那么我就不能变成别人的别名了

引用的特性以及为什么引用不能替代指针:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{int a=10;int &b=a;int &d=b;cout<<&a<<endl;cout<<&b<<endl;cout<<&d<<endl;int e=20;//这里并非让b引用c,因为c++引用不能改变指向//那么这里就是赋值//相当与对a b c d这四个变量进行赋值为e的大小//因为前面四个变量都指向a这块空间//现在我们重新赋值为20//那么下面的输出就是20d=e;cout<<&a<<endl;cout<<&b<<endl;cout<<&d<<endl;cout<<&e<<endl;return 0;
}
/*
C++的引用是不能替代指针的
我们拿链表来举例子的话
链表的节点在物理上并非总是连续的
我们是一个节点存储着下个节点的指针的
如果我们将2这个节点删除的话
那么我们要将1的next指针的指向指向3这个节点
那么通过指针的改变就能实现
但是我们的引用是不能改变指向的
这就是为什么C++的引用不能去替代指针*/

引用的使用

• 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象。

b是a的别名,b没有额外的开辟空间

通过应用的方式减少额外的拷贝:

void Swap(int& rx, int& ry)//形参是实参的别名
//那么rx和ry的交换就是x和y的交换
// 改变引用对象的同时能改变被引用对象
//这里的话指针也是可以实现的,但是指针的话相较于引用更加麻烦
{int tmp = rx;rx = ry;ry = tmp;
}struct A
{int arr[1000];
};void func(A& aa)
{}int main()
{int x = 0, y = 1;cout << x << " " << y << endl;Swap(x, y);cout << x << " " << y << endl;A aa1;func(aa1);//传指针的话我们就直接传过去指针的4个字节//如果将这个结构体传过去的话,那么就是4000个字节,我们需要额外进行拷贝的操作//我们在这里使用引用的话,其实更方便些,同样和指针一样不需要进行额外的拷贝的操作return 0;
}

传值返回也会生成拷贝的

传值返回就是返回的就是拷贝的

传引用返回,返回的就是它的别名

引用做返回值可以减少拷贝做临时对象那一步,

引用减少了返回值的拷贝的步骤:

//传值返回
int STTop(ST& rs)
{assert(rs.top > 0);return rs.a[rs.top];
}
//STTop(st1)++    这个是会报错的
//因为我们在传值返回的时候我们是临时创建了一块空间
//将值传过去,接收完之后这块空间就销毁了
//那么我们这里就无法进行这个返回值加加的操作了
//这个加加操作的话我们加到了临时变量上面去了
//传引用返回
int& STTop(ST& rs)
{assert(rs.top > 0);return rs.a[rs.top];
}
STTop(st1)++;
//我们的返回值的别名
//就是返回我们要返回值的别名
//这样我们就能进行加加操作了//这里我们的返回是返回值的引用
//那么我们这里返回的话就直接跨过了创建临时变量的那一步了

简单点来说就是正常的传值返回的话,我们在返回的时候生成了一个中间变量,通过这个变量我们实现了值的返回

但是我们在使用引用返回的话,我们就不需要这个额外的步骤了

我们直接将返回值的引用值返回了

C++设计引用不是替代指针的,是用来简化指针的问题

不是所有的返回都能用引用返回的

//不是所有的情况都能用引用反回的
int* func()
{int ret = 10;ret++;//......return &ret;//这里我们如果返回的是ret的地址的话,是会报错的,因为ret是个局部变量//出了函数就会被销毁的,那么返回ret的地址就是返回了一个野指针了
}int& func1()
{int ret = 10;ret++;//......return ret;
}
//如果用的是引用返回呢?
//一样是不行的
//因为我们的ret已经别销毁了

为什么在上面的栈写的代码我们可以返回呢

因为我们的数据是在堆上面动态开辟的

我们返回的时候是一直存在的

直到这个我们主动进行free的,否则是一直存在的

那么我们就能进行返回了

那么在这个代码中就不一样,一出函数就被销毁了,指向那块空间的指针就成了野指针了

所以我们在使用引用返回的时候我们是要判断这块空间是否出函数存在

越界是不存在报错的

• 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。

• 引⽤返回值的场景相对⽐较复杂,我们在这⾥简单讲了⼀下场景,还有⼀些内容后续类和对象章节中会继续深⼊讲解。

• 引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的点,C++引⽤定义后不能改变指向,Java的引⽤可以改变指向。

• ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针,但是很多同学没学过引⽤,导致⼀头雾⽔。

关于越界访问:

int main()
{int a[10] = {0};//越界是不一定报错的//越界读的话不报错   读就是打印里面的值cout << a[10] << endl;cout << a[11] << endl;cout << a[12] << endl;//越界写不一定报错    写就是进行赋值a[10] = 1;a[12] = 1;return 0;
}
//编译器系统对越界行为是一种抽查行为
//不一定会报错的

8.const引用

const引用

• 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。

权限的放大和缩小:

int main()
{const int a = 10;//a不能进行修改,所以不能取别名,取别名的话是会对a造成修改的风险的//在引用的过程中权限不能放大//int& ra = a;//下面我们引用一个const对象,但是必须用const引用//那么这里的ra就是const对象const int& ra = a;    //cnost引用普通的对象//权限可以缩小int b = 1;const int& rb = b;//rb不能进行修改,b能被修改//rb++;b++;//不是权限的放大//x不能修改,但是我们在下面将x拷贝成y//我们修改y的话是对x没有影响的const int x = 0;int y = x;//这里是拷贝赋值return 0;
}/*
我们对a进行const的话,那么a的别名就不能进行别名的定义了
因为别名会影响到a,这种属于权限放大了,我们只能进行权限缩小,不能放大但是我们创建了个变量b的话,我们是能对b进行修改的
但是如果b的别名加上const的话就不能进行修改了如果我们一开始就将x进行const定义了
那么我们是能将x赋值给y的
这种属于拷贝
不会对x造成影响的
*/

指针上的权限问题:

int main()
{const int a = 10;//a是不能进行修改的const int* p1 = &a;//p1是不能给p2的,这种属于权限的放大//int* p2 = p1;/*我们的p2一开始就被固定指向为a那么我们就不能修改指向了因为我们前面的加了constconst在*右边就说明我们已经将这个变量进行限制了不能将指向改变了在左边的话就说您我们不能通过指针对指向的数据进行修改的操作*/int b = 20;int* p3 = &b;//p3存的是b的地址const int* p4=p3;//p4也是b的地址,但是我们只能进行读取,不能进行修改的//下面的const在*右边 就说明我们是不能进行指针指向的修改的int* const p4 = &b;int* p5 = p4;return 0;}
/*
不想让const修改p就把p放在*右边-- *const p--一直指向一个数
不想让你通过p修改p的指向的内容就把const放在左边,将*p固定住-conat *p*p被const固定住,*p指向内容的大小不得改变
*/

不想让const修改p就把p放在*右边-- *const p--一直指向一个数 不想让你通过p修改p的指向的内容就把const放在左边,将*p固定住-conat *p

p被const固定住,p指向内容的大小不得改变

• 需要注意的是类似 int& rb = a3; double d = 12.34; int& rd = d; 这样⼀些场景下a3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是时,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。

• 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。

const引用能引用以下对象:

1.普通对象

2.临时对象

3.const对象

不用的引用对象的使用:

void f1(const int& rx)//我们在引用接受对象的同时,为了不能通过这个形参改变实参,我们可以在2前面加上const
{}int main()
{const int xx = 20;int a = 10;//本质而言这里是一个权限放大//int& rb = a * 3;//引用的是这个表达式的整体  ,表达式的结果会用一个临时对象进行存储的const int& rb = a * 3;//加上const就行了//上面的rb引用的是个临时对象  a*3的结果存储在临时变量里面//因为C++规定临时对象具有常性,那么这里就触发了权限放大,必须要用常引用才行//那么前面加上const就行了double d = 12.34;int rd = d;//我们是不能用int类型的数据引用其他类型的数据的//int &rd = d;//这里因为类型不同,那么类型转换的同时中间会生成临时变量,那么我们就需要const进行修饰//这里的临时对象具有常性,和常量一样,就像是已经被const修饰一样//那么我们的左边同样也要进行const修饰的操作,本质是触发了权限的放大const int& rd = d;//前面加上const就行了f1(xx);//可以传const对象f1(a);//可以传普通值f1(a * 3);//可以传表达式f1(d);//可以传带有类型转换的//如果用const引用传参的话是会很宽泛的return 0;
}//临时对象是具有常量性值,就相当与已经被const修饰了。不能被修改//const引用能引用普通对象,引用临时对象,引用const对象

引用和指针的关系

C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。

• 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

• 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。

• 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。

• 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。

• sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)

• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

9.inline

• ⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就不需要建⽴栈帧了,就可以提⾼效率。

宏定义的缺点:

为什么会有这个inline这个东西?

// 实现⼀个ADD宏函数的常⻅问题
//#define ADD(int a, int b) return a + b;
//#define ADD(a, b) a + b;
//#define ADD(a, b) (a + b)
// 正确的宏实现
#define ADD(a, b) ((a) + (b))
// 为什么不能加分号?宏是替换机制的  ,在宏定义后面加分号的话就有问题了
// 为什么要加外⾯的括号?   不加外面的括号这个优先级就有问题了
// 为什么要加⾥⾯的括号?   同样是为了保持优先级int main()
{int ret = ADD(1, 2);//宏是替换机制的cout << ret << endl;;cout << ADD(1, 2) << endl;     //第一个问题cout << ADD(1, 2) * 5 << endl;    //第二个问题int x = 1, y = 2;ADD(x & y, x | y); // -> (x&y+x|y)  第三个问题return 0;
}
/*
宏函数缺点很多,但是替换机制,调用时不用简历栈帧,提效,开销小
*///那么我们的祖师爷忍不了一点儿,就创建了这个inline

• inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。

debug是默认不展开的

release是展开的

大家如果想要展开的话跟随下面的操作图进行操作

可以使debug能展开

1.右击项目点击属性

2.改成下图的样子就行了

3.调整完这个点击应用再确定

展开了就没有栈帧的消耗,不展开就有

如果函数较长的话,就不展开了

inline是一个对于编译器建议,编译器啊才有最终决策权

这个就是编译器的防御策略,怕不靠谱的程序员

• C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调试,C++设计了inline⽬的就是替代C的宏函数。

• vs编译器 debug版本下⾯默认是不展开inline的,这样⽅便调试debug版本想展开需要设置⼀下以下两个地⽅。

• inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。

内联函数不能分离到两个文件,因为内联函数是没有地址的

10.nullptr

NULL实际是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码

#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif

• C++中NULL可能被定义为字⾯常量0,或者C中被定义为⽆类型指针(void)的常量。不论采取何种定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦,本想通过f(NULL)调⽤指针版本的f(int)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。f((void*)NULL);调⽤会报错。

• C++11中引⼊nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数类型。

在c++中使用nullptr来定义空指针可以避免类型转换的问题

用nullptr来定义空指针:

//下面两个函数构成函数重载
void f(int x)
{cout << "f(int x)" << endl;
}
void f(int* ptr)
{cout << "f(int* ptr)" << endl;
}
int main()
{//f(0);//调用第一个//f(NULL);//调用第二个f(nullptr);//在c++中使用nullptr来定义空指针可以避免类型转换的问题return 0;}

相关文章:

手撕C++入门基础

1.C介绍 C课程包括&#xff1a;C语法、STL、高阶数据结构 C参考文档&#xff1a;Reference - C Reference C 参考手册 - cppreference.com cppreference.com C兼容之前学习的C语言 2.C的第一个程序 打印hello world #define _CRT_SECURE_NO_WARNINGS 1 // test.cpp // …...

NPM版本控制策略:实现版本候选行为的指南

引言 在现代JavaScript项目中&#xff0c;依赖管理是确保应用稳定性和安全性的关键环节。NPM&#xff08;Node Package Manager&#xff09;作为Node.js的包管理器&#xff0c;提供了一套灵活的版本控制机制&#xff0c;允许开发者精确控制依赖包的版本。版本候选行为&#xf…...

问题集锦6

1.外调外围接口数据库没有变化 我已经修改完发到线上&#xff0c;看调用用代码释放更新了 or 自己掉测试环境试下 handledList 2.list每次写入最前面 List<Integer> snew ArrayList<>();s.add(1);s.add(2);s.add(0,0);System.out.println(s);3.集合 List<Inte…...

【研发日记】嵌入式处理器技能解锁(四)——TI C2000 DSP的Memory

文章目录 前言 背景介绍 Memory映射 RAM ROM 外设Register Memory分配 应用实例 总结 参考资料 前言 见《【研发日记】嵌入式处理器技能解锁(一)——多任务异步执行调度的三种方法》 见《【研发日记】嵌入式处理器技能解锁(二)——TI C2000 DSP的SCI(串口)通信》 见《…...

Ubuntu离线安装docker

查看操作系统版本&#xff1a; rootzyh-VMware-Virtual-Platform:~/install# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 24.04 LTS Release: 24.04 Codename: noble rootzyh-VMware-Virtual-Platform:~/install#…...

【抓耳挠腮,还是升职加薪,一起来画架构图!】

1. 焦头烂额 最近又遇到个焦头烂额的事情 &#xff0c;老板有了新想法&#xff0c;业务有所转向&#xff0c;需要新的方案设计 &#xff0c;架构设计&#xff0c;以进行后续实施。很快&#xff0c;第一次汇报来了&#xff0c; 由于前期准备时间短&#xff0c;模块拆分不清晰&a…...

算法的学习笔记—合并两个排序的链表(牛客JZ25)

&#x1f600;前言 在算法面试中&#xff0c;链表问题是经常遇到的考点之一&#xff0c;其中合并两个排序链表是一个非常经典的问题。本文将详细介绍如何通过递归和迭代两种方式实现两个有序链表的合并。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f600;合并…...

《虚拟之旅:开启无限可能的机器世界》简介:

1.Ubonto的介绍&#xff1a; Ubuntu 是一个流行的开源操作系统&#xff0c;基于 Linux 内核。 它具有以下一些特点和优势&#xff1a; 开源免费&#xff1a;任何人都可以免费使用、修改和分发。丰富的软件库&#xff1a;通过软件包管理器可以方便地安装各种应用程序。良好的…...

centos7 服务器搭建

1. 查看 centos 版本 cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)2 .查看 ip地址 ip addr sudo yum install net-tools -y 3. 是否能够上网 ping www.baidu.com ping 114.114.114.114 sudo systemctl restart network 4. DNS 更新DNS配置 编辑/etc/r…...

【Godot4自学手册】第四十五节用着色器(shader)制作水中效果

本节内容&#xff0c;主要学习利用着色器制作水波纹效果&#xff0c;效果如下&#xff1a; 一、搭建新的场景 首先我们新建场景&#xff0c;根节点选择Node2D&#xff0c;命名为Water&#xff0c;给根节点添加两个Tilemap节点&#xff0c;一个命名为Background主要用于绘制地…...

VMware Workstation Pro 安装 Ubuntu Server

这里写目录标题 VMware Workstation Pro 安装 Ubuntu Server1. 启动选项2. 系统语言3. 安装程序升级4. 键盘配置5. 安装类型6. 网卡配置7. 代理配置8. 系统镜像配置9. 硬盘配置10. 账户配置11. Ubuntu Pro 版本12. SSH 服务13. 推荐软件14. 安装成功15. 第一次重启报错16. 登录…...

智能化包括自动化与非自动化

智能化通常指的是系统或设备具备智能功能&#xff0c;以提高其自主性和效率。智能化可以分为自动化与非自动化两大类&#xff0c;每一类都有其独特的特点和应用场景。 一、自动化 自动化指的是系统能够在无需人为干预的情况下完成任务或操作。自动化系统通常依赖于预设的规则、…...

微前端架构的容器化部署:策略、实践与优势

随着微服务架构的兴起&#xff0c;微前端架构也成为现代Web应用开发的热门趋势。容器化技术&#xff0c;以其轻量级、可移植性和易于管理的特点&#xff0c;成为微前端部署的理想选择。本文将详细介绍微前端架构下应用容器化部署的策略、实践步骤以及这一方法的优势。 容器化技…...

面试题(网络、js、框架)

自我介绍 您好&#xff0c;面试官&#xff01;我叫[您的姓名]&#xff0c;非常荣幸能有机会参加这次面试。 在过去的 3 年里&#xff0c;我一直专注于前端开发领域&#xff0c;积累了丰富的实践经验。 在 Vue.js 项目中&#xff0c;我能够熟练运用组件化开发模式&#xff0c;实…...

C语言典型例题40

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 题目 例题3.8 运输公司对用户计算运费。路程&#xff08;以s表示&#xff0c;单位为千米&#xff09;&#xff0c;吨/千米运费越低。标准如下&#xff1a; s<250 没…...

【大模型部署及其应用 】使用 Ollama 和 Ollama WebUI 在本地运行 Llama 3

使用 Ollama 和 Ollama WebUI 在本地运行 Llama 3 目录 开始使用 Llama 3设置 Ollama WebUI访问 Ollama WebUI使用 Docker GenAI Stack 的 Llama 3骆驼 2 与 骆驼 3...

uniapp-部分文件中文乱码

一、问题 在开发时遇到&#xff0c;部分页面的中文显示乱码&#xff0c;如图 搜索了一下解决方法&#xff0c;这里记录一下 二、问题原因&#xff1a; 页面的编码格式不是 utf-8 造成的 三、解决方法 打开出现乱码页面选择编译器左上角的文件 > 以指定编码重新打开 选择U…...

Day41 | 647. 回文子串 516.最长回文子序列

语言 Java 647. 回文子串 回文子串 题目 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 思路 动规五部曲来分析 1.dp数组的含义&#x…...

全面解析Gerapy分布式部署:从环境搭建到定时任务,避开Crawlab的坑

Gerapy分布式部署 搭建远程服务器的环境 装好带docker服务的系统 Docker:容器可生成镜像&#xff0c;也可拉去镜像生成容器 示例&#xff1a;将一个环境打包上传到云端(远程服务器)&#xff0c;其他8个服务器需要这个环境直接向云端拉取镜像生成容器,进而使用该环境,比如有MYS…...

Springboot项目中使用druid实现多数据源和动态数据源,因数据库不可用导致的项目挂起的处理方案

Springboot项目中使用druid因数据库不可用导致的项目挂起的处理方案 在Spring Boot项目中使用Druid实现多数据源和动态数据源管理是一个常见的场景。通过合理的配置和错误处理机制&#xff0c;您可以有效地管理数据源&#xff0c;避免因数据库不可用而导致整个项目挂起。 1.…...

多线程 03:知识补充,静态代理与 Lambda 表达式的相关介绍,及其在多线程方面的应用

一、概述 记录时间 [2024-08-16] 前置知识&#xff1a;Java 基础篇&#xff1b;Java 面向对象 多线程 01&#xff1a;Java 多线程学习导航&#xff0c;线程简介&#xff0c;线程相关概念的整理 多线程 02&#xff1a;线程实现&#xff0c;创建线程的三种方式&#xff0c;通过多…...

机器学习中的距离概念

距离在机器学习中应用广泛&#xff0c;包括欧式距离、曼哈顿距离、内积距离和KL距离。 下面总结一下。 机器学习中的距离 欧式距离曼哈顿距离内积距离KL距离距离作为损失函数(MSE/MAE...)欧式距离与内积距离的联系☆距离的有效性 欧式距离 欧式距离&#xff08;Euclidean Dis…...

Java 如何判断map为null或者空

1.示例一 在Java中&#xff0c;如果我们想判断一个Map是否为null或者空&#xff08;即没有任何键值对&#xff09;&#xff0c;我们可以使用以下的方法。下面是一个完整的示例代码&#xff0c;展示了如何进行这样的判断&#xff1a; import java.util.HashMap; import java…...

终端用户视角下的性能测试,体验与度量的融合

传统的性能测试的度量标准是什么 响应时间&#xff08;Response Time&#xff09;&#xff1a; 这是从客户端发出请求到接收到完整响应所需的时间。响应时间是衡量系统性能的重要指标&#xff0c;特别是在面向用户的应用中&#xff0c;因为它直接影响用户体验。 而用户体验的度…...

KCP源码解析系列(二)KCP协议结构体

一、KCP协议包 1.1 kcp协议包 kcp中只有一种数据包&#xff0c;不管是数据还是控制信息&#xff0c;都用这个数据包来表示 0 4 5 6 8 (BYTE) ---------------------------- | conv |cmd|frg| wnd | ---------------------------- 8 | …...

微软运行库全集合:一站式解决兼容性问题

开发者在部署应用程序时经常遇到因缺少运行库而引发的兼容性问题。为了解决这一问题&#xff0c;电脑天空推荐微软常用运行库合集&#xff0c;一个集成了微软多个关键运行库组件的软件包。 &#x1f4da; 包含组件概览&#xff1a; Visual Basic Virtual Machine&#xff1a;…...

【 亿邦动力网-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …...

算法笔记|Day26贪心算法IV

算法笔记|Day26贪心算法IV ☆☆☆☆☆leetcode 452. 用最少数量的箭引爆气球题目分析代码 ☆☆☆☆☆leetcode 435. 无重叠区间题目分析代码 ☆☆☆☆☆leetcode 763.划分字母区间题目分析代码 ☆☆☆☆☆leetcode 452. 用最少数量的箭引爆气球 题目链接&#xff1a;leetcode …...

CVPR2023《DNF: Decouple and Feedback Network for Seeing in the Dark》暗光图像增强论文阅读笔记

相关链接 论文链接 https://openaccess.thecvf.com/content/CVPR2023/papers/Jin_DNF_Decouple_and_Feedback_Network_for_Seeing_in_the_Dark_CVPR_2023_paper.pdf 代码链接 https://github.com/Srameo/DNF 摘要 RAW数据的独特属性在低光照图像增强方面展现出巨大潜力。…...

大厂进阶七:React状态管理全解析

前言&#xff1a; React 中用于状态管理的hook及库有&#xff1a;useState、useReducer、useContext、useReducer useContext和一些第三方的库如redux、mobx等。 1、useState 单一组件某个具体状态 2、useReducer 单一组件中多个状态管理&#xff0c;策略分发机制统一管理…...

【ocr识别003】flask+paddleocr+bootstrap搭建OCR文本推理WEB服务

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 2.了解、学习OCR相关技术知识领域&#xff0c;结合日常的场景进行测试、总结。如本文总结的flaskpaddleocrbootstrap搭建OCR文本推理WEB服务应用示例场景。 文章目录 1.代码结构2.效果演…...

从零开始搭建 LVS 高性能集群 (DR模式)

从零开始搭建 LVS 高性能集群 &#xff08;DR模式&#xff09; 架构 本设计方案采用三台服务器构建集群&#xff0c;使用Linux Virtual Server (LVS) 作为负载均衡器&#xff0c;运行在直接路由 (DR) 模式下。集群中的每一台服务器都将运行相同的服务&#xff0c;以实现 高可用…...

Linux环境开发工具【yum与vim】

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 文章目录 1.Linux软件包管理器yum1.1 快速使用yum 2. Linux编辑器-vim的使用2.1 vim的基本…...

laravel GuzzleHttp Client 无法获取返回的错误信息

Client发送一些请求&#xff0c;当返回状态不是200的时候&#xff0c;无法获取完整错误信息 $client new Client([base_uri > $this->getUri()./order/aaaaaa,timeout > 30,verify > false]);try {$response $client->request(POST, , [headers > [Lang&g…...

XMOS 多路音频解码器

当谈及高性能音频解码器&#xff0c;XMOS 是一个不容忽视的名字。作为音频解决方案领域的领军者&#xff0c;XMOS 的多路音频解码器在音频处理技术中扮演着至关重要的角色。下面我们一起深入探讨 XMOS 多路音频解码器去了解这一技术的魅力。 设计背景&#xff1a;追求音频极致…...

XSS小游戏(题目+解析)

xss题目练习地址&#xff1a; xss小游戏 游戏界面 一、Ma Spaghet! 我将题目要求进行翻译: 题目的主要要求就是&#xff1a;弹出一个&#xff08;1337&#xff09;的弹窗 开始解题&#xff1a; Let’s Go! 首先&#xff0c;传个参数看看 发现参数直接显示在了 < h2 >…...

《Redis核心技术与实战》学习笔记4——AOF日志:宕机了,Redis如何避免数据丢失?

文章目录 AOF 日志是如何实现的&#xff1f;三种写回策略 日志文件太大了怎么办&#xff1f;AOF 重写会阻塞吗?小结 大家好&#xff0c;我是大白。 如果有人问你&#xff1a;“你会把 Redis 用在什么业务场景下&#xff1f;”我想你大概率会说&#xff1a;“我会把它当作缓存使…...

NextJs - 服务端/客户端组件之架构多样性设计

NextJs - 服务端/客户端组件之架构多样性设计 前言一. 架构设计1.1 SSR流式渲染常见错误设计之 - 根页面同步阻塞1.2 架构设计之 - 客户端组件依赖于服务端组件数据① 使用 Redux 完成数据共享 1.3 架构设计之 - 单页内的分步骤跳转① 如何做到服务端组件和客户端组件之间的切换…...

使用 Python 进行 PDF 文件加密

使用 Python 解密加密的 PDF 文件-CSDN博客定义一个名为的函数&#xff0c;该函数接受三个参数&#xff1a;输入的加密 PDF 文件路径input_pdf、输出的解密 PDF 文件路径output_pdf和密码password。https://blog.csdn.net/qq_45519030/article/details/141256661 在数字化时代…...

Spring Boot集成RabbitMQ

目录 1.RabbitMQ简介2.添加依赖3.配置RabbitMQ连接4.DirectExchange4.1 消费者4.2 生产者4.3 测试4.4 一个交换机对多个队列4.5 一个队列对多个消费者 5.FanoutExchange5.1 消费者5.2 生产者5.3 测试 6.TopicExchange6.1 消费者6.2 生产者 1.RabbitMQ简介 RabbitMQ是一个由Erl…...

OLED屏幕制造工艺流程

OLED屏幕制造工艺流程是一个复杂且精细的过程&#xff0c;涉及多个关键步骤以确保最终的显示效果和性能。以下是OLED屏幕制造工艺流程的主要步骤&#xff1a; 1. 衬底制作与准备 材料选择&#xff1a;OLED器件需要一个透明的导电衬底&#xff0c;通常使用玻璃或塑料材料。 清…...

knowLedge-VueCLI项目中环境变量的定义与使用

1. env 1.1简介 在 Vue CLI 创建的项目中&#xff0c;你可以通过 .env 文件来定义环境变量。Vue CLI 支持多种 .env 文件&#xff0c;它们根据文件名中的前缀来决定何时加载和使用这些环境变量。 以下是一些常见的 .env 文件及其用途&#xff1a; .env&#xff1a;在任何环境…...

【C#】 接口 继承

简介 继承是面向对象编程的核心特性之一&#xff0c;它允许我们创建一个类&#xff08;称为子类&#xff09;来继承另一个类&#xff08;称为基类&#xff09;的属性和方法。 作用 这样&#xff0c;我们可以重用代码&#xff0c;减少重复&#xff0c;并使我们的代码更加模块…...

Self-Supervised Learning(李宏毅老师系列)

自学参考&#xff1a; BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding BERT 论文逐段精读 视频课 课件资料 笔记 一、概述 自监督学习模型与芝麻街~ 参数量 ELMO&#xff1a;94MBERT&#xff1a;340MGPT-2&#xff1a;1542MMegatron&…...

8月16日笔记

只有DNS协议出网场景 DNS 协议是一种请求、应答协议&#xff0c;也是一种可用于应用层的隧道技术。DNS 隧道的工作原理很简单&#xff0c;在进行 DNS 查询时&#xff0c;如果查询的域名不在 DNS 服务器本机缓存中&#xff0c;就会访问互联网进行查询&#xff0c;然后返回结果。…...

苹果Mac电脑——装macOS和Windows双系统的方法

一、实验环境 在Windows系统电脑上制作MacOS启动U盘。准备一个大于16G的U盘。 二、实验步骤 2.1 在Windows系统电脑上制作MacOS启动U盘 MacOS镜像下载 在Windows系统电脑上制作MacOS启动U盘的方法 2.2 U盘插上苹果电脑&#xff0c;安装macOS系统 U盘插上苹果电脑&#xf…...

【C++ 面试 - 基础题】每日 3 题(十五)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…...

数学建模学习笔记

数学建模学习笔记 现学现卖&#xff0c;随缘更新QwQ 主要根据b站大师兄的视频整理而成&#xff0c;有不懂的可以去看原视频 List 数学建模学习笔记一、 层次分析法1.1 矩阵的一致性及其检验1.2 权重计算1.3 具体流程 二、模糊综合评测2.1 隶属函数2.2 隶属函数的确定方法2.3 模…...

个人可识别信息(PII) AI 去除 API 数据接口

个人可识别信息(PII) AI 去除 API 数据接口 ai / 隐私保护 基于 AI 模型自动去除个人识别信息&#xff08;PII&#xff09; 个人信息保护 / AI 模型 。 1. 产品功能 基于自有专业模型进行 PII 自动去除高效处理敏感信息全接口支持 HTTPS&#xff08;TLS v1.0 / v1.1 / v1.2 /…...

【Python-办公自动化】1秒提取PPT文本内容形成目录保存至WORD

欢迎来到"花花 Show Python",一名热爱编程和分享知识的技术博主。在这里,我将与您一同探索Python的奥秘,分享编程技巧、项目实践和学习心得。无论您是编程新手还是资深开发者,都能在这里找到有价值的信息和灵感。 自我介绍: 我热衷于将复杂的技术概念以简单易懂…...