初识C++编程语言(万字详解)

目录

::域作用限定符

命名空间域(namespace):

流插入和流提取(C++的输入输出)

缺省参数:

函数重载: 

引用:

内联函数:

auto关键字:

1、类型思考:

2、auto介绍: 

auto的使用细则 :

1. auto与指针和引用结合起来使用

2. 在同一行定义多个变量

3、auto不能使用的场景 

基于范围的for循环(C++11) 

1 、范围for的语法

 C++98中的指针空


::域作用限定符

在之前单纯的C语言中,同一个域里无法取得相同的名字这是很不方便的,在大型的工程中不同的程序员不可能做到所有变量都不相同,所以::域作用限定符应运而生。

例如:

 这种命名方式是不被允许的,在同一个域里,不能命名两个一样的变量。

#include<stdio.h>
int x = 0;
int main() {int x = 1;printf("%d\n", x);return 0;
}

所以我们可以向上面一样,将一个变量放在全局,一个放在局部。这样就不会报命名冲突的问题了,但是如果我们打印这个x变量,会发现屏幕上打印出来的是1。

为什么呢?🤔编译器会首先在局部域里找x,然后在去全局域里找,如果在局部域里找到了,自然就直接打印x了。

那么我们如果想要打印全局变量的这个0呢,我们可以这样:

#include<stdio.h>
int x = 0;
int main() {int x = 1;printf("%d\n", x);printf("%d\n", ::x);return 0;
}

 这时候就体现出了::的作用,如果::的左边什么都没有,那就是默认全局域,所以编译器会在全局域里找变量x。


命名空间域(namespace):

😎是为了解决命名空间里命名冲突的问题。

#include<stdio.h>namespace project1 {int x = 1;
}
namespace project2 {int x = 2;
}int main() {printf("%d\n", project1::x);printf("%d\n", project2::x);return 0;
}

我们如果想要命名同一个名字的话,我们就可以这样,用命名空间域来隔开,格式为:namespace+空间域名字。这样我们就可以用域空间操作符来指定使用变量名称了。

编译器搜索原则:

无指定:1、当前局部域  2、全局域  

有指定:1、直接在指定域中搜索

命令空间域里可以是:1、变量,2、函数,3、结构体。

#include<stdio.h>namespace project1 {int x = 1;
}
namespace project2 {//变量int x = 2;//函数int Add(int a,int b){return a + b;}//结构体struct ListNode {/*....*/}LNode;
}int main() {printf("%d\n", project1::x);printf("%d\n", project2::x);project2::Add(1,2);struct project2::ListNode phead;return 0;
}

需要注意的是:使用命名空间域里的结构体时,域名要在struct后结构体名前。

namespace还有一个使用方法:

当我们需要频繁调用project2时,就需要一直调用,就会变得很麻烦,所以我们介绍namespace的第二种用法:

这个叫做展开命名空间,也就是说使用了这个之后,project2就被展开了,被展开成了全局变量,再使用这个命名空间域里的东西的时候,就可以不用再引用了。

被展开的域相当于全局变量,所以当有局部变量的时候依然会优先访问局部变量。 

所以,当我们使用C++的时候,通常为了方便,会先写using namespace std,std就是C++的库,是为了方便调用C++库里面函数或者是关键字的。 (但是也仅仅是在我们平时的练习测试用的多,主要是为了方便)。

还有一种写法就是我可以展开这个库的一部分,也就是说:

就像这样,把几个常用的给展开,就可以了。

这时候有人要提出问题了,如果命名域的名字也冲突了怎么办?🤔

答:命名域允许嵌套,也就说如果命名域真的冲突了,我们也可以命明域进行嵌套来解决,而且如果我们想,我们可以无限的套娃下去。但一般我们嵌套两层就够用了。

可以看到这里我们在用的时候,就需要用两次::域作用限定符 。


流插入和流提取(C++的输入输出)

#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}

说明:
1. 使用cout标准输出对象(控制台)cin标准输入对象(键盘)时,必须包含< iostream >头文件
以及按命名空间使用方法使用std
2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< 
iostream >头文件中。
3. <<是流插入运算符,>>是流提取运算符
4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。
C++的输入输出可以自动识别变量类型。

#include <iostream>
using namespace std;
int main()
{int a;double b;char c;// 可以自动识别变量的类型cin>>a;cin>>b>>c;cout<<a<<endl;cout<<b<<" "<<c<<endl;return 0;
}

// ps:关于cout和cin还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等等。因为C++兼容C语言的用法,这些又用得不是很多,所以如果输出输入浮点数,我们还是采用C语言来实现 

std命名空间的使用惯例:
std是C++标准库的命名空间,如何展开std使用更合理呢?
1. 在日常练习中,建议直接using namespace std即可,这样就很方便。
2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对
象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + 
using std::cout展开常用的库对象/类型等方式。


缺省参数:

#include<iostream>
using namespace std;void Fun(int a = 10, int b = 20, int c = 30) {cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;}
int main() {Fun(1,2,3);Fun(1,2);Fun(3);Fun();return 0;
}

 当我们来写一个函数的时候,如果我们传参了,那么就跟正常的一样,如果没传参,那么就会默认的用我们括号里面的值。

上面是缺省参数的四种形式,传参的时候如果我们想要修改其中的参数可以只传它的参数,没传参的就会默认括号里的值,但是不能进行跳跃传参,比如我们想只传b但是a,c还用默认值的话,这是办不到的。

还有一个是半缺省的形式, 像上面的b,我们可以不给默认值,但是这时候传参的话就必须传值了。

举一个实际的例子:

#include<iostream>
using namespace std;struct Stack{int* a;int size;int capacity;
};
void StackInit(struct Stack* ps) {//ps->a = NULL;ps->a = (int*)malloc(sizeof(int) * 100);
}int main() {struct Stack st1;StackInit(&st1);return 0;
}

我们假设初始化栈的时候,开辟栈的大小使用malloc,但是当我们有以下三种情况的时候:

这种情况下我们又怎么进行动态开辟呢,大小该给多少呢?

这时候我们的缺省参数就派上用场了:

#include<iostream>
using namespace std;struct Stack{int* a;int size;int capacity;
};
void StackInit(struct Stack* ps ,int n = 4) {//ps->a = NULL;ps->a = (int*)malloc(sizeof(int) * n);
}int main() {struct Stack st1;//确定要插入100个数据StackInit(&st1,100);struct Stack st2;//确定要插入10个数据StackInit(&st2,10);struct Stack st3;//不知道要插入多少的时候StackInit(&st3);return 0;
}

 这样是不是就很方便呢。

那当声明与定义分离的时候,该在哪里给缺省参数呢?

答案是:声明。                                                    
首先声明和定义肯定是不能同时给缺省参数的,因为当两个缺省参数不同时,就会报错。

其次,缺省参数是在调用的时候用的,那么将缺省参数放在声明才不会在编译的时候报错。

最后,缺省值必须是常量或者全局变量。


函数重载: 


C语言中我们不能命名相同名字的函数,在C++中我们可以命名相同函数的名字,但前提是:函数的参数的个数,类型,顺序不一样。

C语言做不到的,C++又是如何做到的呢?

C++明确了在调用的时候,不仅仅可以用函数的名字,还可以加上函数的参数类型合并起来在进行判断是否是重名的,并且还加上了参数数量的比较,就比如上面的两个Add,他们在编译完之后一个是Addci,另一个是Adddd。所以最后他们就是不同的函数。

但这里只是一个例子,不同的编译器会有不同的实现方式,但是思路都是一样的。


引用:

C语言中,我们如果想要指向一个变量的话,我们会将他的地址取出来存到指针变量里面,但是在针对一些复杂指针的时候,就很不方便,所以C++采用了引用。

#include<iostream>
using namespace std;
int main() {int a = 1;//引用int& b = a;cout << a << endl << b << endl;return 0;}

引用的形式就是:&(取地址符),如上面代码int& b = a,此时b就是a的别称,例如周树人的笔名是鲁迅,那么鲁迅和周树人都是指同一个人。所以引用的作用,就是给a取了一个别名b,那么无论输出a还是输出b,结果都是1。

#include<iostream>
using namespace std;
//这是我们C语言中的交换函数,必须传递实参的地址。
void Swap(int* x, int* y) {int tmp = *x;*x = *y;*y = tmp;
}
//这是用引用的写法,这里的x,y就是两个实参的别名
void Swap(int& x, int& y) {int tmp = x;x = y;y = tmp;
}
int main() {int a = 1;int c = 5;cout << a <<"<->" << c << endl;//Swap(a, c);Swap(&a, &c);cout << a << "<->" << c << endl;Swap(a, c);cout << a <<"<->" << c << endl;return 0;}

交换函数正常的实现,这就是引用的用法。

注意事项:

1、引用必须初始化,不可以写成:

int& b;
b = c;

 2、引用定义后不能改变指向,不能写成:

int& b = a;
int c = 3;
b = c; //这里就不是改变指向,而是赋值

3、 一个变量可以有多个引用,多个别名

int main() {int a = 1;int& b = a;int& d = a;int& e = a;return 0;}

这里面的bde都是a的别名。

既然引用的作用与指针作用重叠。

那么有小伙伴要提问了,那引用不是就替代指针了?🤔🤔🤔

 C++的引用是为了解决指针使用复杂场景的一些替换,让代码更简单易懂,但不能完全代替。

引用不能替代指针的原因:引用定义后不能改变指向。

例如:

 在双向链表中,删除中间的结点,该删除结点的前一个和后一个要改变指向,这是引用做不到的。

引用还有更为广泛的应用就是:

void PushBack(struct ListNode** phead,int x) {//........
}
void PushBack(struct ListNode*& phead, int x) {//........
}
int main() {struct ListNode* pList = NULL;
}

假如我们在PushBack函数里要对指针进行修改,那么我们就必须传一个二级指针(如第一个写法),那有了引用之后(第二个写法)ListNode*phead就是ListNode*pList的一个别名,所以当然可以在PushBack函数进行修改了。

还有一种经常出现在书上的写法让一些刚学C语言的小伙伴们十分头疼:

typedef struct ListNode {ListNode* prev;ListNode* next;int val;
}LNode, *PNode;void PushBack(PNode& phead, int x) {//........
}
int main() {PNode pList = NULL;return 0;
}

这个*PNode就是struct ListNode的指针,所以指针类型就直接是PNode,然后再结合上&引用,就实现了这个的参数类型的写法。

然后我们来一个简易的顺序表感受一下C和C++的不同:

#include<iostream>
#include<assert.h>
using namespace std;struct SeqList {int* a;int size;int capacity;
};
void SLInit(SeqList& sl) {sl.a = (int*)malloc(sizeof(int) * 4);sl.size = 0;sl.capacity = 4;
}
void SLModity(SeqList& sl, int pos,int x) {assert(pos >= 0);assert(pos < sl.size);sl.a[pos] = x;
}
void SLPushBack(SeqList& sl, int x) {sl.a[sl.size++] = x;
}
int SLGet(SeqList& sl, int pos) {assert(pos >= 0);assert(pos < sl.size);return sl.a[pos];
}
int main() {SeqList sl;SLInit(sl);SLPushBack(sl, 1);SLPushBack(sl, 2);SLPushBack(sl, 3);SLPushBack(sl, 4);for (int i = 0; i < sl.size; i++) {cout << SLGet(sl, i) <<" ";}cout << endl;for (int j = 0; j < sl.size; j++) {int val = SLGet(sl, j);if (val % 2 == 0) {SLModity(sl, j, val * 2);}}for (int i = 0; i < sl.size; i++) {cout << SLGet(sl,i) << " ";}return 0;}

这个代码嵌了一点点C++,但是跟C差不多。

#include<iostream>
#include<assert.h>
using namespace std;struct SeqList {int* a;int size;int capacity;void Init() {a = (int*)malloc(sizeof(int) * 4);size = 0;capacity = 4;}void PushBack(int x) {a[size++] = x;}int& Get(int pos) {assert(pos >= 0);assert(pos <= size);return a[pos];}
};int main() {SeqList sl;sl.Init();sl.PushBack(1);sl.PushBack(2);sl.PushBack(3);sl.PushBack(4);for (int i = 0; i < sl.size; i++) {cout << sl.Get(i) <<" ";}cout << endl;for (int j = 0; j < sl.size; j++) {if (sl.Get(j) % 2 == 0) {sl.Get(j)*= 2;}}for (int i = 0; i < sl.size; i++) {cout << sl.Get(i) << " ";}return 0;}

这里如果用int不加引用的话,相当于返回的是一个临时拷贝,临时变量是建立在堆上的,并且临时变量具有常性(可以理解为被const修饰过的),那么我们肯定是没办法修改的。就算这里不考虑常性,那修改的也只是这个临时变量,并没有修饰到顺序表中的本体,但是加了引用之后,不产生临时变量,引用就相当于返回了他的别名,那么修改别名就是修改了本体。所以用引用返回值具有有了读写功能。

引用和指针的区别:

语法层面:

1、引用是别名,不开空间,指针是地址需要开空间。

2、引用必须初始化,指针可以初始化也可以不初始化。

3、引用不能改变指向,指针可以。

4、引用相对更安全,没有空引用,但是空指针,容易出现野引用,但是不容易出现野指针。

5、sizeof、++、解引用访问等上面的区别。

底层: 

汇编层面上,没有引用只有指针,编译后引用也变成了指针。

在汇编层面上,引用和指针是没有区别的。 


内联函数:

C语言调用函数是需要开销的,假如说C语言有一个频繁调用的小函数:

int Add(int a, int b) {return a + b;
}

假如这个相加的小函数需要调用100w次,那就需要建立100w个栈帧空间。

那C语言是如何解决这个问题的:宏函数。

#define Add(a,b) ((a)+(b))

但是宏有很多缺点:

1、语法复杂,坑较多,不容易控制。

2、不能调试。

3、没有安全类型的检查。 

所以C++引出了内联函数:inline

inline int Add(int a, int b) {return a + b;
}

他的功能其实是和宏差不多,在多次调用的时候,不需要每次都建立栈帧。

并且优化了宏的缺点 。

inline函数的特性:

1、内联函数是一种时间换空间的做法,如果编译器把函数当作内联函数使用的时候,在编译过程中,编译器会把函数体替换为函数调用。缺点:函数体展开使目标文件变大。(当函数体较长,再去多次调用多次展开,会出现代码膨胀)优点:减少函数调用开销,提高代码运行效率。

2、将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰。

3、inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到。

// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{f(10);return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl 
//f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

在C++中是比较排斥使用宏的,一般会采用以下方法来替代宏:

1、常量定义换用const、enum

2、短小函数定义换用inline


auto关键字:

1、类型思考:

随着我们学习的深入,变量的类型会越来越复杂,这就导致我们在使用的时候变得十分麻烦。

#include <string>
#include <map>
int main()
{std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange", "橙子" }, {"pear","梨"} };std::map<std::string, std::string>::iterator it = m.begin();while (it != m.end()){//....}return 0;
}

std::map<std::string, std::string>::iterator 是一个类型,但是该类型太长了,特别容易写错。

2、auto介绍: 

auto 作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。言简意赅就是auto可以根据变量的值自动推导变量的类型。

int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}

【注意】
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型 。

auto的使用细则 :

1. auto与指针和引用结合起来使用

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

int main()
{int x = 10;auto a = &x;auto* b = &x;auto& c = x;cout << typeid(a).name() << endl;cout << typeid(b).name() << endl;cout << typeid(c).name() << endl;*a = 20;*b = 30;c = 40;return 0;
}

2. 在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。 

void TestAuto()
{auto a = 1, b = 2; auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

3、auto不能使用的场景 

1. auto不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

2. auto不能直接用来声明数组 

void TestAuto()
{int a[] = {1,2,3};auto b[] = {4,5,6};
}

基于范围的for循环(C++11) 

1 、范围for的语法

 在C++98中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)cout << *p << endl;
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)e *= 2;
for(auto e : array)cout << e << " ";
return 0;
}

 范围for相当于是依次取数组里面的值赋值给e,然后再将e输出,auto也是能更好的自动识别类型,以应对各种类型的数组,&是为了如果想要改变数组内容,那么我们引用就把e变成了数组每个元素的别名,就可以修改数组里面的值了。

注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。

void TestFor(int array[])
{for(auto& e : array)cout<< e <<endl;
}

 上面这个代码是不能用范围for的,现阶段范围for只能用于数组,但是在上面的TestFor函数中,array不是数组,而是一个指针。


 C++98中的指针空值

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}

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

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

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如: 

void f(int)
{cout<<"f(int)"<<endl;
}
void f(int*)
{cout<<"f(int*)"<<endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);return 0;
}

 程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

 所以从C++11以来用nullptr来填上了这个小坑。

#include<iostream>
using namespace std;
void f(int)
{cout << "f(int)" << endl;
}
void f(int*)
{cout << "f(int*)" << endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);f(nullptr);int* p = nullptr;return 0;
}

注意:
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。 


 

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

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

相关文章

【二十九】springboot高并发示例

本章演示在springboot项目中的高并发demo&#xff0c;演示导致的问题&#xff0c;以及单机部署下的解决方案和集群部署下的解决方式以及分布式下的解决方案。 目录 一、单机模式下高并发问题 二、集群模式下高并发问题 一、单机模式下高并发问题 前提&#xff1a;先写一个减扣…

腾讯云轻量服务器流量用完了怎么办?停机吗?

腾讯云轻量服务器流量用完了怎么办&#xff1f;超额流量另外支付流量费&#xff0c;流量价格为0.8元/GB&#xff0c;会自动扣你的腾讯云余额&#xff0c;如果你的腾讯云账号余额不足&#xff0c;那么你的轻量应用服务器会面临停机&#xff0c;停机后外网无法访问&#xff0c;继…

创建数据表

Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 如果要进行数据表的创建 create table 表名称 (列名称 类型 [DEFAULT 默认值 ] ,列名称 类型 [DEFAULT 默认值 ] ,列名称 类型 [DEFAULT 默认值 ] ,...列名称 类型 [DEFAULT 默认值 ] )…

Yolov8涨点神器:添加可变形大核注意力,超越自注意力,实现暴力涨点(附代码)

目录 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; 可变形大核注意力(D-LKA Net) deformable_LKA引入到yolov8 核心代码 yaml文件 DeformableConv2d ⭐欢迎大家订阅我的专栏一起学习⭐ &a…

力扣由浅至深 每日一题.01 两数之和

万物惊鸿&#xff0c;唯我澄明 —— 24.3.9 1. 两数之和https://leetcode.cn/problems/two-sum/ 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会…

猫头虎分享已解决Bug || 系统监控故障:MonitoringServiceDown, MetricsCollectionError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

“比特币深夜冲破7万美元”!华尔街押注比特币:究竟是牛市墙头草,还是加密真信徒?

比特币ETF&#xff0c;使此次加密牛市与以往的繁荣、萧条周期截然不同。以往的周期往往由热衷风险的投机者以及最终崩盘的加密项目所驱动&#xff0c;例如无实物资产支持的加密货币借贷&#xff0c;以及一地鸡毛的ICO热潮。而现在&#xff0c;传统金融已经与加密世界联姻&#…

从零到一,构建坚如磐石的Redis 7高可用集群:全程实录与关键技术详解

1、引言 在日常的开发中&#xff0c;无论是主从复制还是哨兵模式&#xff0c;都在高并发的场景中存在致命的缺点&#xff1a; 主从复制&#xff1a;当Master Redis机器挂掉之后&#xff0c;Slave依旧可以读取数据&#xff0c;但是由于Master不能写数据了&#xff0c;所以就会…

python自动化测试如何做数据缓存 ?这个第三方包推荐给你,方便又简单!

1.数据缓存说明 数据缓存可以说也是项目开发中比不可少的一个工具 &#xff0c;像我们测试的系统中 &#xff0c;你都会见到像Redis一样的数据缓存库 。使用缓存数据库的好处不言而喻&#xff0c;那就是效率高 &#xff0c;简单数据直接放在缓存中 &#xff0c;存取简单方便 。…

每日OJ题_牛客CM26 二进制插入

目录 牛客CM26 二进制插入 解析代码 牛客CM26 二进制插入 二进制插入_牛客题霸_牛客网 解析代码 class BinInsert { public:int binInsert(int n, int m, int j, int i) {// 1 2 4 8 16 32 64 128 256 512 1014// 1024 2 4 32 // 1024 4 8 64 1024 76// 1000000000…

面向切面编程(AOP)介绍(横切关注点、通知(增强)、连接切入点、切面)

1. 面向切面编程思想AOP AOP&#xff1a;Aspect Oriented Programming面向切面编程 AOP可以说是OOP&#xff08;Object Oriented Programming&#xff0c;面向对象编程&#xff09;的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构&#xff0c;用于模拟公…

JavaEE进阶(15)Spring原理:Bean的作用域、Bean的生命周期、Spring Boot自动配置(加载Bean、SpringBoot原理分析)

接上次博客&#xff1a;JavaEE进阶&#xff08;14&#xff09;Linux基本使用和程序部署&#xff08;博客系统部署&#xff09;-CSDN博客 目录 关于Bean的作用域 概念 Bean的作用域 Bean的生命周期 源码阅读 Spring Boot自动配置 Spring 加载Bean 问题描述 原因分析 …

Intellij IDEA 中 git 操作的快捷键

1.添加新建的文件 即add 操作 shift alt a 2.提交操作 即 commit操作 ctrl k 在窗口中可以用feature来声明此次更新的内容 3.提交操作 即push操作 ctrl shift k 4.拉去远程分支操作 即pull操作 ctrl t

Spring学习 基础(三)MVC

5、Spring MVC 传统Web模式&#xff1a; Model:系统涉及的数据&#xff0c;也就是 dao 和 bean。View&#xff1a;展示模型中的数据&#xff0c;只是用来展示。Controller&#xff1a;处理用户请求都发送给 &#xff0c;返回数据给 JSP 并展示给用户。 随着 Spring 轻量级开发…

C if 语句

一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 语法 C 语言中 if 语句的语法&#xff1a; if(boolean_expression) {/* 如果布尔表达式为真将执行的语句 */ }如果布尔表达式为 true&#xff0c;则 if 语句内的代码块将被执行。如果布尔表达式为 false&#xff0c;…

poll开发服务器

int poll(struct pollfd *fds, nfds_t nfds, int timeout); 函数说明&#xff1a;与select类似&#xff0c;委托内核监控可读&#xff0c;可写&#xff0c;异常事件。 函数说明&#xff1a; fds&#xff1a;一个struct pollfd结构体数组的首地址 struct pollfd { …

天猫淘宝详情接口API揭秘:实现个性化商品推荐!

天猫淘宝作为中国最大的电商平台&#xff0c;拥有庞大的商品库存和众多用户&#xff0c;为了提高用户购物体验并满足其个性化需求&#xff0c;天猫淘宝推出了详情接口API&#xff0c;通过智能算法实现个性化商品推荐。联讯数据将为您揭秘详情接口API的实现原理和功能。 个性化商…

信息抽取在旅游行业的应用:以景点信息抽取为例

开源项目推荐 今天先给大家推荐一个开源项目&#xff0c;多模态AI能力引擎平台: 免费的自然语言处理、情感分析、实体识别、图像识别与分类、OCR识别、语音识别接口&#xff0c;功能强大&#xff0c;欢迎体验。 https://gitee.com/stonedtx/free-nlp-api 场景描述 在旅游行业…

【新书推荐】17.3 字符串操作举例

本节内容&#xff1a;字符串操作举例。 ■例3&#xff1a;接收字符串去掉空格后逆向输出&#xff0c;t17-3.asm。 ■例4&#xff1a;字符串拼接&#xff0c;t17-4.asm。 ■例5&#xff1a;判断子串&#xff0c;t17-5.asm。 17.3.1 例3&#xff1a;接收字符串去掉空格后逆向输出…

如何关闭 Visual Studio 双击高亮

[问题描述]&#xff1a; 最近 Visual Studio 更新后&#xff0c;双击选中关键字快要亮瞎我的眼睛了 &#x1f440;&#x1f440; [解决方法]&#xff1a; 摸索了一下&#xff0c;找到了关闭的方法&#xff1a;工具 → 选项 → 文本编辑器 → 常规&#xff0c;然后取消 勾选 sel…