【C++】| C/C++内存管理

前言:

在上期,我们已经对类和对象的全部知识进行了总结和梳理。在类和对象学习完之后,今天我将给大家呈现的是关于——C/C++内存管理的基本知识。

【C++】C/C++内存管理(new和delete详解)- 惊觉

 


本文目录

1. C/C++内存分布

2. C语言中动态内存管理方式

(1)C语言跟内存分配方式

(2)C语言跟内存申请相关的函数

(3)面试题

3. C++中动态内存管理

3.1 new/delete操作内置类型

3.2 new和delete操作自定义类型

4. operator new与operator delete函数😎

5. new和delete的实现原理

5.1 内置类型

5.2 自定义类型

6. 定位new表达式(placement-new)

7. 常见面试题

7.1 malloc/free和new/delete的区别

8.总结


1. C/C++内存分布

C++的内存管理其实还是延续了C语言的内存管理的规则,首先第一点带大家解答为什么需要内存管理?🤔

  • 因为在程序里面需要不同类型或者不同性质的数据,那这些不同的数据是存在不同的区域,例如我之前讲过内存中有堆区,栈区,静态区等不同的区域。
  • 有这些区域的本质原因,即是对于不同的数据类型有不同的特性,我们为了方便更好的对其进行管理呢,就会把一些相同特性的数据分到同一区域进行管理。

接下来,我们通过习题的方式带大家来具体的理解其中的关系:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
 static int staticVar = 1;
 int localVar = 1;
 int num1[10] = { 1, 2, 3, 4 };
 char char2[] = "abcd";
 const char* pChar3 = "abcd";
 int* ptr1 = (int*)malloc(sizeof(int) * 4);
 int* ptr2 = (int*)calloc(4, sizeof(int));
 int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
 free(ptr1);
 free(ptr3);
}

1. 选择题:
   选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)

   globalVar在哪里?____   staticGlobalVar在哪里?____
   staticVar在哪里?____   localVar在哪里?____
   num1 在哪里?____
   
   char2在哪里?____       *char2在哪里?___
   pChar3在哪里?____      *pChar3在哪里?____
   ptr1在哪里?____        *ptr1在哪里?____

解析如下:

  • 【globalVar】:  globalVar作为全局变量在数据段(静态区)
  • 【staticGlobalVar】:staticGlobalVar作为静态全局变量在静态区
  • 【staticVar】:staticVar作为静态局部变量在静态区
  • 【localVar】:  localVar作为局部变量局部变量放在栈区
  • 【num1】:  num1为局部变量一样也存放在栈区

以上这个五个我相信大多数小伙伴都可以作对的,接下来我们讲解下面几个:

  • 【char2】:  char2是一个数组,跟num1的区别是(num1是自己确定大小,而char2是通过初始化来确定大小),所以不难得出num1和char2是在一个地方的,即——作为局部变量,放在栈区  
  • 【*char2】:char2是一个数组,把后面常量字符串拷贝过来到数组中,数组在栈上,所以*char2在栈上
  • 【pChar3】: pChar3局部变量在栈区
  • 【* pChar3】: *pChar3得到的是字符串常量字符在代码段
  • 【ptr1】:  ptr1是指针,作为局部变量栈区   
  • 【* ptr1】: *ptr1就是ptr指向的那块空间,因此得到的是动态申请空间的数据在堆区

接下来,我们结合图形,大家可以直观的感受!!

 接下来,还有个连环的问题,大家在看看下列的题目,不知道各位是否能够拿下它呢?

 sizeof(num1) = ____;  

 sizeof(char2) = ____;   strlen(char2) = ____;

 sizeof(pChar3) = ____;   strlen(pChar3) = ____;

 sizeof(ptr1) = ____;

解析:

  •   sizeof(num1) = __40__; sizeof数组名,即计算数组大小,10个整形数据一共40字节
  •   sizeof(char2) = __5__; 包括\0的空间,因此为5
  •   strlen(char2) = __4__; 遇到\0即截止,不包括\0的长度,因此为4
  •   sizeof(pChar3) = __4__; 因为pChar3为指针,所以跟后面指向的美誉关系,32位下为大小4,64位下8
  •   strlen(pChar3) = __4__; 字符串“abcd”的长度,不包括\0的长度
  •   sizeof(ptr1) = __4__; ptr1是指针,同上

【说明】🤫

  • 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  • 2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
  • 3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  • 4. 数据段--存储全局数据和静态数据。
  • 5. 代码段--可执行的代码/只读常量。

通过上述的问题带大家仔细再次认识了一下程序在内中的分布问题。接下来,我们将探讨关于动态内存管理方式的问题!!!


2. C语言中动态内存管理方式

这个知识点,我们在之前就已经具体的讲到过了,在这里给大家简单的在过一遍。这里给大家一段代码,大家先回顾一下之前有关的知识:

void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);

// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);

// 这里需要free(p2)吗?
free(p3 );
}

(1)C语言跟内存分配方式

  • <1>从静态存储区域分配.
  • 内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量、static变量.
  • <2>在栈上创建
  • 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限.
  • <3>从堆上分配,亦称动态内存分配.
  • 程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存.动态内存的生存期由用户决定,使用非常灵活,但也很容易出现问题

(2)C语言跟内存申请相关的函数

C语言跟内存申请相关的函数主要有calloc、malloc、free、realloc等.

  •  <a>malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间.
  • <b>calloc则将初始化这部分的内存,设置为0.
  • <c>realloc则对malloc申请的内存进行大小的调整.
  • <d>申请的内存最终需要通过函数free来释放.

切记:

  •  当程序运行过程中malloc了,但是没有free的话,会造成内存泄漏.一部分的内存没有被使用,但是由于没有free,因此系统认为这部分内存还在使用,造成不断的向系统申请内存,使得系统可用内存不断减少.

对于malloc详细的知识可以参考如下地址:

malloc函数

对于calloc详细的知识可以参考如下地址:

calloc函数

对于realloc详细的知识可以参考如下地址:

realloc函数


(3)面试题

接下来给大家解答一个常见的面试题——malloc/calloc/realloc的区别?

 区别:


    (1)函数malloc不能初始化所分配的内存空间,而函数calloc能

  • 如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;
  • 反之, 如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据.
  • 也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间已经被重新分配)那么就可能会出现问题.


    (2)函数calloc() 会将所分配的内存空间中的每一位都初始化为零

  • 也就是说,如果你是字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;
  • 如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;
  • 如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零.


    (3)函数malloc向系统申请分配指定size个字节的内存空间.返回类型是 void*类型.

  • void*表示未确定类型的指针.C,C++规定,void* 类型可以强制转换为任何其它类型的指针.


    (4)realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.

  • 当然,如果是缩小,则被缩小的那一部分的内容会丢失.realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址.
  • 相反,realloc返回的指针很可能指向一个新的地址.


    (5)realloc是从堆上分配内存的.

  • 当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;
  • 如果数据后面的字节不够,问题就出来了,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上.这句话传递的一个重要的信息就是数据可能被移动.

3. C++中动态内存管理

3.1 new/delete操作内置类型

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:

  • 通过new和delete操作符进行动态内存管理。

首先第一个知识点来了,那就是小伙伴们知道默认情况下【new】会不会初始化呢?

我通过调试带大家仔细瞧瞧是不是像我所说的那样:

  •  从上述我们不难发现,不管是对于【new】定义的【p1】来说,还是通过传统的【malloc】方式定义的【p2】来说,默认都是没有进行初始化操作的!!!

那么大家就会好奇了,【new】是否可以初始化呢?

  • 答案当然是可以的,接下来我带大家看看具体的操作。

 此时,一个可能让大家混淆的点就出现了,大家是否能够区别下述这种方法呢?😖

int* p1 = new int(1);//初始化

int* p3 = new int[10];

大家是否知道这两个的区别呢?不知道没关系,接下来我给大家解答一下:

  • 对于【int* p1 = new int(1);】:这是进行初始化操作,申请一个(int)类型,并初始化为1;
  • 对于(int* p3 = new int[10];):它的意思动态申请10个int类型的空间
  • 大家一定区分二者之间的差别,不要搞混淆了!!

 那对于(int* p3 = new int[10];)这种情况,我们是否还能对其初始化呢?其实也是可以的,C++支持这样的操作,具体如下所示:

	int* p4 = new int[10] {1, 2, 3, 4};

 当我们想对其进行初始化时,只需在后面加上【{}】即可,那么是不是呢?我通过调试给大家展示:

 

我们从上述不难看出,当我们用【new】时是不是比我们用【malloc】方便得多呀!

  • 对于【malloc】我们不仅需要强转类型,还需要进行检查

还是通过代码,大家就可以一目了然两者之间的差别到底有多大:

 注意:

  1. 此时很多小伙伴或许就会问,难道【new】不会失败吗?其实是会的,它失败的机制跟【malloc】是不一样的,它通过“抛异常”来进行错误判别的,我们后面会讲到。

我用一张图给大家总结,大家看下表就能直观的理解上面的知识了:

 特别注意一点:💣

  • 申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用 new[]和delete[],注意:匹配起来使用

3.2 new和delete操作自定义类型

上述我们已经知道一点,new/delete 和 malloc/free对于内置类型的处理几乎是一样的,但是对于自定义类型是否也是一样的呢?

我们还是通过代码来大家理解这个问题,大家看以下代码,最终的结果是什么呢?

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

int main()
{
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);

	free(p1);
	delete p2;

		return 0;
}

解析:

我们直接打印看看最后的结果是什么。

  •  通过上述不知道大家有没有发现一个点呀!那就是此时这里我们是定义的两个,怎么只调用了一次构造和析构函数呢?是谁没有调用呢?接下来,通过调试,我带大家一步一步的去查看

 解析:

  • 第一步,首先是对【p1】进行的操作,此时当执行完【p1】之后,我们发现并没有去调用构造函数。

  解析:

  • 此时,当我们去执行完【p2】之后,我们发现,此时程序就去调用了构造函数,并且成功的完成了初始化操作。继续执行

   解析:

  • 当执行完毕,对其进行释放的时候,我们可以发现,程序是先对【p1】进行的释放,但是此时并没有去调用析构函数。

   解析:

  • 最后,当我们对【p2】进行释放的时候,此时程序调用了析构函数,并且也成功的把【p2】释放掉了。

因此,综上所述:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与 free不会。


4. operator new与operator delete函数😎

  • 上述我们已经知道对于 【new】和【delete】是用户进行动态内存申请和释放的操作符;
  • 而接下来要学习的operator new 和operator delete是系统提供的全局函数;
  • new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。

大家是不是一看到这个就以为是函数重载呀!在之前我们已经学习过,【new】是函数,而【operator】是重载的符号:

  • 其实不然啊,大家千万不要这么理解。这里的两个函数是库里面提供的两个全局函数,不是运算符重载哟!!!
  • 取这个名字给大家造成了极大的误解,至于为什么要这么定义呢?我们也不得而知了,可能我们的祖师爷在设计的时候没有想到好名字,这就造成了许多学习【C++】的在这里吃了一个亏。

接下来带大家浅浅的看一下库里面是怎么实现这两个函数的,以下为库里面的代码(看不明白没关系)

对于【operator new】,库里面是这么写的:

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
  if (_callnewh(size) == 0)
     {
         // report no memory
         // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
         static const std::bad_alloc nomem;
         _RAISE(nomem);
     }
return (p);
}

解析:

  • 大家看代码,我们可以看到operator new里面是不是调用的【malloc】啊,只是它这里跟【malloc】不同的是,紧接着往下看我们可以发现,上述代码【malloc】之后赋值给了【p】,然后判断,如果【p】等于0的话就调用【(_callnewh(size) == 0】这个函数,然后进入里面进行了“抛异常”,即【malloc】失败了就会抛异常。

而对于【operator delete】,库里面是这么写的:

/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
          /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
     return;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

解析:

  • 上面那一大串的大家都可以不用看,看最后的一两行,最后是不是显示的【free】啊!

因此,我们可以得出一个结论:

  • 通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。
  • operator delete 最终是通过free来释放空间的。
  • 因此,【operator delete】和【operator new】本质上就是【delete】和【new】的封装。

接下来就给大家讲解其中的原因?

首先,我们怎么使用者两个函数呢?其实吧,这两个函数的实现跟【malloc】是类似的,我们举例观察:

解析:

  • 对于上述的【p1】,我们使用的是【operator new】,我们不难发现跟【malloc】的实现方式很类似。
  • 只是底层的机制不一样罢了。对于【malloc】实现,会进行严格的检查,而对于【operator new】则是失败后“抛异常”!!

紧接着大家是否好奇为什么会有这两个函数呢?

  • 我们都知道【c++】兼容C语言,就拿我们之前以及讲过的“引用”为例,当在【C++】中为了实现这个会去单独的创造一套语法啊什么的出来吗?应该不会吧。引用底层是按指针的方式来实现引用,那么这时还有必要再去创造新的东西呢?结果可能而知。
  • 现在我们来对比一下【new】的实现,第一步是申请空间紧接着就是调用构造函数对于申请空间是不是就是去堆上申请啊,在【C】语言中我们也是从堆上申请的呀!而在【C】语言中,对于申请空间,是不是就要调用【malloc】。
  • 而对于【C++】来说,因为它是面向对象的语言,虽然兼容C语言,但是新增的那部分是面向对象的,而面向对象的语言处理异常使用的是抛异常”。而在【C】语言中,对于失败,返回的是【null】,就不符合我们的需求,所以C++就用operator new去封装【malloc】,封装【malloc】失败之后“抛异常”!!!🥶

我们在通过反汇编的角度去看看,不难发现底层确实像我们所说的那样:


 

5. new和delete的实现原理

5.1 内置类型

  • 如果申请的是内置类型的空间,new和malloc,delete和free基本类似.
  • 不同的地方是: new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申 请空间失败时会抛异常,malloc会返回NULL

5.2 自定义类型

new的原理

  • 1. 调用operator new函数申请空间
  • 2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理

  • 1. 在空间上执行析构函数,完成对象中资源的清理工作
  • 2. 调用operator delete函数释放对象的空间

new T[N]的原理

  • 1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对 象空间的申请
  • 2. 在申请的空间上执行N次构造函数

delete[]的原理

  • 1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  • 2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释 放空间

6. 定位new表达式(placement-new)

作用:定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。

使用格式:

  • new (place_address) type或者new (place_address) type(initializer-list)
  • place_address必须是一个指针,initializer-list是类型的初始化列表

我们先看一下以下代码:

int main()
{
	A aa;

	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == nullptr)
	{
		perror("malloc fail");
	}
	return 0;
}

解析:

此时,我们这里有一块已经【malloc】出来的空间,此时当我们在想对其进行初始化时是不行的

 

此时对于【C++】来说,对已经有一块空间的进行初始化,此时应该怎么办呢?基于这种情况,就引入了——定位new

new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参

new(p1)A(1);  

接下来,我们通过调试带大家去看看:

 

解析:

  • 对于没有参数的时候,调试出来的我们发现也没有参数。

 解析:

  • 对于有参数的时候,调试出来的我们对其进行了初始化操作。

当我们想去调用析构函数时,我们可以显示的去调用是可以的,具体如下:

int main()
{
	A aa;

	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == nullptr)
	{
		perror("malloc fail");
	}

	//new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参

	new(p1)A(1);  

	p1->~A(); //调用析构函数,显示的去调用
	free(p1);


	return 0;
}

输出结果为:

 

大家看完这个是不是会觉得十分麻烦呀!这么复杂搞得,我们直接【new】不是更好吗?具体如下:

	A* p2 = new A;
  • 其实确实是这样的,在实际当中对其自定义类型,我们直接用【new】就可以了,没必要再去【malloc】紧接着再去使用这个定位new。

那么何时我们改用这个呢?

  • 定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。 

如果还没明白。接下来我讲个故事,大家可以类比的进行理解:

  • 在以前,假如还没有水缸之类的东西用于存储,假如此时我们要煮饭,我们就去水井里舀水回来,总之就是当我们想使用水时时就要去水井里盛水回来;
  • 现在引入内存池,意思就相当于现在有装水的容器了,我们可以一次性盛许多水回来,当我们想用时就不用再每次去水井里盛水了,只有当装水的容器里没了之后,我们才去水井里盛水。

7. 常见面试题

7.1 malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:

  • 都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:

  • 1. malloc和free是函数,new和delete是操作符
  • 2. malloc申请的空间不会初始化,new可以初始化
  • 3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
  • 4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  • 5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需 要捕获异常
  • 6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new 在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成 空间中资源的清理

8.总结

以上便是关于C/C++内存管理的问题,还有部分关于内存泄漏的问题,我们到后面在讲。最后,总结一下本期的内容。

对于内存管理呢,C/C++其实是类似的,唯独在C++中引入了【new】和【delete】机制。

并且建议使用【new】和【delete】机制,原因有二:

  • 第一是因为用起来方便许多相比于【malloc】这种方式;
  • 第二个原因针对自定义类型,【new】和【delete】能更好的调用构造函数和析构函数,而【malloc】则不满足这种场景。

到此,便是关于内存管理的所有内容。如果感觉对您有帮助的话,麻烦点赞三连哟!!!

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

php科研项目申报审批系统

目 录 1 绪论 4 1.1 开发背景 4 1.2 开发意义 4 1.3 相关知识介绍 4 1.3.1 Apache 4 1.3.2 MySQL 5 1.3.3 PHP 6 1.3.4 Dreamweaver CS3 7 1.4 本文所做的工作及组织结构 7 2 系统分析 7 2.1 需求分析 7 2.2 可行性分析 7 2.3 系统界面…

CSDN博客专家证书发放名单(2023年3月已更新)

目录 证书发放频次 6月&#xff08;第一批&#xff09;证书发放名单&#xff08;80位&#xff09; 7月&#xff08;第二批&#xff09;证书发放名单&#xff08;50位&#xff09; 8月&#xff08;第三批&#xff09;证书发放名单&#xff08;54位&#xff09; 9月&#xf…

2个月月活突破1亿,增速碾压抖音,出道即封神的ChatGPT,现在怎么样了?ChatGPT它会干掉测试?

从互联网的普及到智能手机&#xff0c;都让广袤的世界触手而及&#xff0c;如今身在浪潮中的我们&#xff0c;已深知其力。 前阵子爆火的ChatGPT&#xff0c;不少人保持观望态度。现如今&#xff0c;国内关于ChatGPT的各大社群讨论&#xff0c;似乎沉寂了不少&#xff0c;现在…

Prometheus监控实战之Exporter详解

1 exporter是什么&#xff1f; 广义上向prometheus提供监控数据的程序都可以成为一个exporter的&#xff0c;一个exporter的实例称为target, exporter来源主要2个方面&#xff0c;一个是社区提供的&#xff0c;一种是用户自定义的。 2 常用exporter 官方和一些社区提供好多ex…

彻底关闭Windows自动更新

彻底关闭Windows自动更新 目录 彻底关闭Windows自动更新 前言 Windows10彻底关闭自动更新方法步骤&#xff1a; 一、禁用Windows Update服务 二、在组策略里关闭Win10自动更新相关服务 三、禁用任务计划里边的Win10自动更新 四、在注册表中关闭Win10自动更新 前言 我们用…

易语言支持库配置闪退丨支持库配置崩溃_易语言打开支持库配置就闪退怎么办?

易语言支持库配置闪退打不开怎么办&#xff1f; 易语言支持库配置闪退解决方法丨支持库配置崩溃_易语言打开支持易语言支持库配置闪退丨支持库配置崩溃_易语言打开支持库配置就闪退怎么办? 很多人都遇到过 打开易语言支持库安装菜单报错退出的问题 今天教大家解决方法 我们…

4.2--Redis总结之高可用篇(关于哨兵机制)---(温故而知新篇)--加油呀

1.为什么要有哨兵机制&#xff1f; 在 Redis 的主从架构中&#xff0c;由于主从模式是读写分离的&#xff0c;如果主节点挂了&#xff0c;那么将没有主节点来服务客户端的写操作请求&#xff0c;也没有主节点给从节点进行数据同步了 哨兵机制&#xff0c;它的作用是实现主从节…

spring集成mybaits以注解方式完成连表查询练习

题目&#xff1a; 以注解的方式完成连表查询 1、查询全部员工信息&#xff0c;要求显示部门名称 2、根据姓名模糊查询员工信息 自己看完&#xff0c;建一个简单的员工表和部门表。 1、先创建一个maven模块&#xff0c;这里直接建的普通…

SpringMVC --- 获取请求参数、域对象共享数据、视图

一、SpringMVC获取请求参数 1.1、通过ServletAPI获取 将 HttpServletRequest 作为控制器方法的形参&#xff0c;此时 HttpServletRequest 类型的参数表示封装了当前请求的请求报文的对象 RequestMapping("/param/servletAPI")public String getParamByServletAPI(H…

从零开始实现一个C++高性能服务器框架----配置模块

此项目是根据sylar框架实现&#xff0c;是从零开始重写sylar&#xff0c;也是对sylar丰富与完善 项目地址&#xff1a;https://gitee.com/lzhiqiang1999/server-framework 简介 项目介绍&#xff1a;实现了一个基于协程的服务器框架&#xff0c;支持多线程、多协程协同调度&am…

【C++】多态(下)

文章目录1.单继承中的虚函数表整体代码用程序打印虚表如何寻找到虚表地址虚表存在哪里&#xff1f;2.多继承中的虚函数表整体代码寻找虚表地址注意事项多继承重写后的func1的地为什么地址不同&#xff1f;ptr1调用函数——一次jmpptr2 调用函数——多次jmp1.单继承中的虚函数表…

window环境 python ide 安装教程分享

一、 右键-以管理员身份运行 python.exe&#xff08;以安装 3.8 的为例&#xff0c;安 装方法是一样的哈&#xff09; 二、选择你的安装方式。 特别注意&#xff1a;需要把 Add Python ** to PATH 勾选上 ②Customize installation 是自定义安装&#xff0c;安装位置你可以自己…

链表【左程云:Java】

一、单链表 1.单链表的节点结构 2.反转单向和双向链表 2.1 反转单向 package leetcode.链表;/*** author lin* creat 2022--12--12:50** https://leetcode.cn/problems/reverse-linked-list/*/ public class $_206反转链表 {public class ListNode {int val;ListNode next;L…

基于VHDL语言的汽车测速系统设计_kaic

摘 要 汽车是现代交通工具。车速是一项至关重要的指标。既影响着汽车运输的生产率,又关乎着汽车行驶有没有超速违章&#xff0c;还影响着汽车行驶时人们的人身安全。而伴随着我国国民的安全防范意识的逐步增强&#xff0c;人们也开始越来越关心因为汽车的超速而带来的极其严重…

一份sql笔试

1、 select substr(time,1,10),count(order_id),count(distinct passenger_id) from order where substr(time,1,7)2023-08 group by substr(time,1,10) order by substr(time,1,10);2、 select city_id from (select * from order where substr(time,1,7) 2022-08) t1 left j…

【新2023Q2押题JAVA】华为OD机试 - 打折买水果

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:打折买水果 题目 有 m m m…

Spring之属性填充

Spring给属性的方式一般有三种 1、通过在属性的添加Autowired注解 Component public class UserService {Autowiredprivate OrderService orderService;public void setOrderService(OrderService orderService) {this.orderService orderService;}public OrderService getO…

b站第一,Python自动化测试实战详细教学,3天教你学会自动化测试

目录 简介 Python自动化测试概述 Python自动化测试目标 Python自动化测试流程 1. 测试计划和设计 2. 测试脚本开发 3. 测试执行和管理 4. 测试维护和优化 Python自动化测试最佳实践 Python自动化测试工具和框架 结论 简介 自动化测试是软件开发过程中一个必不可少的…

【Django 网页Web开发】22. 实战项目:简单的文件上传(15)(保姆级图文)

目录实现效果1. url.py2. upload_list.html3. upload.py总结欢迎关注 『Django 网页Web开发』 系列&#xff0c;持续更新中 欢迎关注 『Django 网页Web开发』 系列&#xff0c;持续更新中 实现效果 1. url.py path(upload/list/, upload.upload_list),2. upload_list.html {% e…

Python中进程和线程到底有什么区别?

人生苦短&#xff0c;我用python python 安装包资料:点击此处跳转文末名片获取 一、进程和线程的关系 线程与进程的区别可以归纳为以下4点&#xff1a; 地址空间和其它资源&#xff08;如打开文件&#xff09;&#xff1a;进程间相互独立&#xff0c;同一进程的各线程间共享。…