C++初阶(八)--内存管理

news2024/11/7 5:38:56

目录

引入:

一、C++中的内存布局

1.内存区域

2.示例变量存储位置说明

二、C语言中动态内存管理

三、C++内存管理方式 

1.new/delete操作内置类型

2.new和delete操作自定义类型

四、operator new与operator delete函数(重要点进行讲解)

五、new和delete的实现原理

六、定位 new 和定位 delete

七、面试题

1.malloc/free和new/delete的区别?

 2.内存泄漏


引入:

以下代码分别存储在哪个区域

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";
	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);
}

 图解:

一、C++中的内存布局

在了解具体的内存管理操作之前,先得清楚 C++ 程序的内存布局。一般来说,一个 C++ 程序的内存可以大致分为以下几个区域:

1.内存区域

  1. 内核空间

    • 用户代码不能读写。
    • 操作系统内核运行在此空间,负责管理系统资源和执行关键任务。
  2. 代码区(Text Segment)

    • 存放程序的可执行代码,即机器指令。
    • 通常是只读的,防止程序在运行过程中意外修改自身的代码。
  3. 全局 / 静态存储区(Data Segment)

    • 存储全局变量和静态变量。
    • 全局变量在整个程序的生命周期内都存在,而静态变量(包括局部静态变量)根据其定义的作用域有不同的可见性,但它们的生命周期都是从程序开始到结束。
  4. 栈区(Stack)

    • 用于存储函数调用时的局部变量、函数参数以及返回地址等信息。
    • 遵循后进先出(LIFO)的原则,每当一个函数被调用时,相关的信息就会被压入栈中,函数执行完毕后,这些信息又会被弹出栈。
    • 栈的大小通常是在程序启动时就确定好的,相对有限,如果在栈上分配过多的内存,可能会导致栈溢出的错误。
  5. 堆区(Heap)

    • 是一块相对较大且比较灵活的内存区域,用于动态分配内存。
    • 程序员可以在程序运行期间根据需要随时在堆上申请和释放内存。
    • 与栈不同,堆上的内存分配和释放需要程序员手动进行管理,这也正是内存管理容易出现问题的地方之一。如果忘记释放堆内存,会导致内存泄漏。
  6. 常量存储区

    • 存放常量数据,如字符串常量等。
    • 这些数据在程序运行期间不能被修改。
  7. 内存映射段

    • 可以用于文件映射、动态库加载、匿名映射等。
    • 提供了一种将文件或其他资源映射到进程内存空间的方式,以便更高效地访问这些资源。

2.示例变量存储位置说明

1、globalVar在哪里?

根据上面的代码可知,glovalVar是在main函数外创建的变量,即在全局创建的变量,全局变量存放在数据段(静态区)中。

2、staticGlobalVar在哪里?

staticGlobalVar是在main函数外创建的静态变量,即在全局创建的静态变量,全局静态变量存放在数据段(静态区)中。
3、staticVar在哪里?

staticVar是在main函数内部创建的静态变量,即在局部创建的静态变量,局部静态变量存放在数据段(静态区)中。

4、localVar在哪里?

localVar是在main函数内部创建的变量,即在局部创建的普通变量,局部创建的普通变量存放在栈区。
5、char2在哪里?

char2是在main函数内部创建的数组的数组名,即在局部创建的多个普通变量,局部创建的普通变量存放在栈区。

6、* char2在哪里?

*char2是对数组的的首元素进行解引用,解引用的值存放在哪个区域,*char2的则存放在哪个区域,*char2是数组的第一个字符,即字符变量中的第一个元素,字符变量存放在栈区,因此*char2存放在栈区。
7、pChar3在哪里?

pChar3是在main函数内部创建的const修饰的常指针变量,实质还是一个局部创建的变量,只是该变量的值不能修改,因此pChar3存放在栈区。

8、* pChar3在哪里?

*pChar3是对数组的的首元素进行解引用,解引用的值存放在哪个区域,*pChar3的则存放在哪个区域,*pChar3是常量字符串的第一个字符,字符常量存放在代码段(常量区),因此*pChar3存放在代码段(常量区)。
9、ptr1在哪里?

ptr1是在main函数内部创建的指针变量,实质还是一个局部创建的变量,因此pChar3存放在栈区。(ptr2、ptr3同理)

10、* ptr1在哪里?

*ptr1是对数组的的首元素进行解引用,解引用的值存放在哪个区域,*ptr1的则存放在哪个区域,*ptr1是通过动态开辟的空间,动态开辟的空间存放在堆区,因此*ptr1存放在堆区。(ptr2、ptr3同理)

顺便提一下:为什么说栈是向下增长的,而堆是向上增长的? 

简单来说,在一般情况下,在栈区开辟空间,先开辟的空间地址较高,而在堆区开辟空间,先开辟的空间地址较低。

例如,下面代码中,变量a和变量b存储在栈区,指针c和指针d指向堆区的内存空间:

#include <iostream>
using namespace std;
int main()
{
	//栈区开辟空间,先开辟的空间地址高
	int a = 10;
	int b = 20;
	cout << &a << endl;
	cout << &b << endl;

	//堆区开辟空间,先开辟的空间地址低
	int* c = (int*)malloc(sizeof(int)* 10);
	int* d = (int*)malloc(sizeof(int)* 10);
	cout << c << endl;
	cout << d << endl;
	return 0;
}

 

因为在栈区开辟空间,先开辟的空间地址较高,所以打印出来a的地址大于b的地址;在堆区开辟空间,先开辟的空间地址较低,所以c指向的空间地址小于d指向的空间地址。

注意:在堆区开辟空间,后开辟的空间地址不一定比先开辟的空间地址高。因为在堆区,后开辟的空间也有可能位于前面某一被释放的空间位置。

二、C语言中动态内存管理

1.malloc:

malloc函数的功能是开辟指定字节大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个NULL。传参时只需传入需要开辟的字节个数。

2.calloc

calloc函数的功能也是开辟指定大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个NULL。calloc函数传参时需要传入开辟的内存用于存放的元素个数和每个元素的大小。calloc函数开辟好内存后会将空间内容中的每一个字节都初始化为0。

3.realloc 

realloc函数可以调整已经开辟好的动态内存的大小,第一个参数是需要调整大小的动态内存的首地址,第二个参数是动态内存调整后的新大小。realloc函数与上面两个函数一样,如果开辟成功便返回开辟好的内存的首地址,开辟失败则返回NULL。

 4.free

free函数的作用就是将malloc、calloc以及realloc函数申请的动态内存空间释放,其释放空间的大小取决于之前申请的内存空间的大小。

这里只做简单的概述,若还想进一步了解malloc、calloc、realloc和free,请阅读动态内存管理。

三、C++内存管理方式 

 C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

1.new/delete操作内置类型

// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
//动态申请10个int类型的空间并初始化为0到9
int* p7 = new int[10] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //申请 + 赋值

delete ptr4;//销毁
delete ptr5;
delete[] ptr6;
delete[] p7;

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

2.new和delete操作自定义类型

对于以下自定义类型:

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

int main()
{

	// 动态申请单个类的空间
	A* ptr4 = new A;
	// 动态申请一个A类的空间并初始化为10
	A* ptr5 = new A(10);
	// 动态申请10个A类的空间,创建 10 个对象
	A* ptr6 = new A[10];
	//动态申请10个A类的空间并初始化0到9
	A* ptr7 = new A[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; //申请 + 赋值

	delete ptr4;//销毁
	delete ptr5;
	delete[] ptr6;
	delete[] ptr7;

}

 给一个malloc版本

int main()
{
	// 动态申请单个类的空间
	A* ptr4 = (A*)malloc(sizeof(A));
	// 动态申请一个 A 类的空间并初始化为 10
	A* ptr5 = (A*)malloc(sizeof(A));
	// 动态申请 10 个 A 类的空间
	A* ptr6 = (A*)malloc(sizeof(A) * 10);
	// 动态申请 10 个 A 类的空间并初始化 0 到 9
	A* ptr7 = (A*)malloc(sizeof(A) * 10);
	
	free(ptr4);
	free(ptr5);
	free(ptr6);
	free(ptr7);

	return 0;
}

 可以自己进行调试一下,会发现malloc,free和new,delete的区别。

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会。

总结一下:
 1、C++中如果是申请内置类型的对象或是数组,用new/delete和malloc/free没有什么区别。
 2、如果是自定义类型,区别很大,new和delete分别是开空间+构造函数、析构函数+释放空间,而malloc和free仅仅是开空间和释放空间。
 3、建议在C++中无论是内置类型还是自定义类型的申请和释放,尽量都使用new和delete。

 四、operator new与operator delete函数(重要点进行讲解)

new和delete在底层上就是调用operator newoperator delete的。

operator newoperator delete是 C++ 中用于动态内存分配和释放的操作符函数。它们可以被重载以实现自定义的内存分配策略。默认情况下,operator new会调用底层的操作系统函数来分配内存,而operator delete会释放由operator new分配的内存。

operator new和operator delete的用法和malloc和free的用法完全一样,其功能都是在堆上申请和释放空间。

	int* p1 = (int*)operator new(sizeof(int)* 10); //申请
	
	operator delete(p1); //销毁

//-------------等价-----------------//
	int* p2 = (int*)malloc(sizeof(int)* 10); //申请
	
	free(p2); //销毁

默认行为:在 C++ 中,当我们使用new关键字来分配内存时,实际上会调用operator new函数。这个函数会尝试从堆中分配足够的内存来满足请求。如果分配成功,它会返回一个指向分配的内存的指针;如果分配失败,它会抛出一个std::bad_alloc异常。

实际上,operator new的底层是通过调用malloc函数来申请空间的,当malloc申请空间成功时直接返回;若申请空间失败,则尝试执行空间不足的应对措施,如果该应对措施用户设置了,则继续申请,否则抛异常。而operator delete的底层是通过调用free函数来释放空间的。

注意:虽然说operator new和operator delete是系统提供的全局函数,但是我们也可以针对某个类,重载其专属的operator new和operator delete函数,进而提高效率。 

例如,我们可以实现一个简单的内存池来提高内存分配的效率。

以下是一个简单的示例:

class MyClass
{
public:
    static void* operator new(size_t size)
    {
        cout << "Custom operator new called." << endl;
        return malloc(size);
    }

    static void operator delete(void* ptr)
    {
        cout << "Custom operator delete called." << endl;
        free(ptr);
    }
};

int main()
{
    MyClass* obj = new MyClass();
    delete obj;
    return 0;
}

在这里,我们重载了MyClass类的operator newoperator delete函数。当我们创建一个MyClass对象时,会调用自定义的operator new函数,该函数使用malloc来分配内存。当我们释放一个MyClass对象时,会调用自定义的operator delete函数,该函数使用free来释放内存。

五、new和delete的实现原理


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

自定义类型
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函数完成N个对象空间的释放。

六、定位 new 和定位 delete

除了普通的operator newoperator delete,C++ 还提供了定位new和定位delete。定位new允许我们在已经分配的内存上构造对象,而定位delete允许我们在已经构造的对象上显式地调用析构函数并释放内存。

定位new(placement new)

定位new允许在已经分配好的内存地址上构造对象,而不是像普通的new操作符那样从堆上动态分配新的内存。它的语法形式是new (place_address) type或者new(place_address)type(initializer-list),其中place_address是一个已经分配好的内存地址,type是要构造的对象类型,initializer-list是类型的初始化列表

定位delete(placement delete)

定位delete通常与定位new配合使用,它允许在已经构造的对象上显式地调用析构函数并释放内存。一般情况下,不需要显式调用定位delete,只有在特定的情况下(比如异常处理中需要确保正确的析构函数调用)才可能会用到。

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

#include <iostream>
#include <new>

class MyClass
{
public:
    MyClass()
    {
        std::cout << "MyClass constructor called." << std::endl;
    }

    ~MyClass()
    {
        std::cout << "MyClass destructor called." << std::endl;
    }
};

int main()
{
    //此时只是开辟空间,没有创建对象,因为构造函数没有调用
    char* buffer = new char[sizeof(MyClass)];
    MyClass* obj = new (buffer) MyClass();   //new(place_address)type 形式
    obj->~MyClass();
    delete[] buffer;

    return 0;
}

代码讲解:

在这个例子中,首先分配了一块足够大的内存空间(通过char* buffer = new char[sizeof(MyClass)];),然后使用定位new在这个已经分配好的内存地址buffer上构造了一个MyClass对象。这里先显式调用对象的析构函数(obj->~MyClass();),然后释放分配的内存块(delete[] buffer)。注意,这里并没有直接使用定位delete,而是通过先调用析构函数再释放内存块的方式来模拟定位delete的行为。

总的来说,定位new和定位delete提供了一种在特定内存位置上构造和销毁对象的机制,在一些特定的场景下可以提供更灵活的内存管理方式。但使用时需要非常小心,确保内存的正确分配和释放,以避免出现内存泄漏和未定义行为等问题。

七、面试题

1.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在释放空间前会调用析构函        数完成空间中资源的清理。

 2.内存泄漏

什么是内存泄漏,内存泄漏的危害?

内存泄漏:

内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害:

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

void MemoryLeaks()
{
	// 1.内存申请了忘记释放
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;

	// 2.异常安全问题
	int* p3 = new int[10];
	Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
	delete[] p3;
}

 内存泄漏分类

在C/C++中我们一般关心两种方面的内存泄漏:
1、堆内存泄漏(Heap Leak)

堆内存指的是程序执行中通过malloc、calloc、realloc、new等从堆中分配的一块内存,用完后必须通过调用相应的free或者delete释放。假设程序的设计错误导致这部分内容没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap
Leak。

 2、系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

 如何避免内存泄漏?

   1、工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记住匹配的去释放。
 2、采用RAII思想或者智能指针来管理资源。
 3、有些公司内部规范使用内部实现的私有内存管理库,该库自带内存泄漏检测的功能选项。
 4、出问题了使用内存泄漏工具检测。

内存泄漏非常常见,解决方案分为两种:
 1、事前预防型。如智能指针等。
 2、事后查错型。如泄漏检测工具。

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

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

相关文章

架构的本质之 MVC 架构

前言 程序员习惯的编程方式就是三步曲。 所以&#xff0c;为了不至于让一个类撑到爆&#x1f4a5;&#xff0c;需要把黄色的对象、绿色的方法、红色的接口&#xff0c;都分配到不同的包结构下。这就是你编码人生中所接触到的第一个解耦操作。 分层框架 MVC 是一种非常常见且常…

Node学习记录-child_process 子进程

来自&#xff1a;https://juejin.cn/post/7277045020422930488 child_process用于处理CPU密集型应用&#xff0c;Nodejs创建子进程有7个API&#xff0c;其中带Async的是同步API,不带的是异步API child_process.exec(command[, options][, callback]) command:要运行的命令&am…

NVR批量管理软件/平台EasyNVR多个NVR同时管理支持对接阿里云、腾讯云、天翼云、亚马逊S3云存储

随着云计算技术的日益成熟&#xff0c;越来越多的企业开始将其业务迁移到云端&#xff0c;以享受更为灵活、高效且经济的服务模式。在视频监控领域&#xff0c;云存储因其强大的数据处理能力和弹性扩展性&#xff0c;成为视频数据存储的理想选择。NVR批量管理软件/平台EasyNVR&…

2024年编程语言排行榜:技术世界的新星与常青树

随着技术的不断进步&#xff0c;编程语言的流行度也在不断变化。今天&#xff0c;就让我们一起来看看2024年的编程语言排行榜&#xff0c;探索哪些语言在技术世界中占据了主导地位。 1. Python&#xff1a;稳居榜首 Python以其在人工智能、数据科学、网络开发等多个领域的广泛…

MFC工控项目实例二十八模拟量信号每秒采集100次

采用两个多媒体定时器&#xff0c;一个0.1秒计时,另一个用来对模拟量信号采集每秒100次.。 1、在SEAL_PRESSUREDlg.h中添加代码 class CSEAL_PRESSUREDlg : public CDialog { public:CSEAL_PRESSUREDlg(CWnd* pParent NULL); // standard constructor&#xff0e;&#xff0e…

基于MoviNet检测视频中危险暴力行为

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【Faster & Mask R-CNN模型实现啤酒瓶瑕疵检测】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生…

ArcGIS003:ArcMap常用操作0-50例动图演示

摘要&#xff1a;本文以动图形式介绍了ArcMap软件的基本操作&#xff0c;包括快捷方式创建、管理许可服务、操作界面元素&#xff08;如内容列表、目录树、搜索窗口、工具箱、Python窗口、模型构建器窗口等&#xff09;的打开与关闭、位置调整及合并&#xff0c;设置默认工作目…

NVR批量管理软件/平台EasyNVR多个NVR同时管理支持视频投放在电视墙上

在当今智能化、数字化的时代&#xff0c;视频监控已经成为各行各业不可或缺的一部分&#xff0c;无论是公共安全、交通管理、企业监控还是智慧城市建设&#xff0c;都离不开高效、稳定的视频监控系统的支持。而在这些应用场景中&#xff0c;将监控视频实时投放到大屏幕电视墙上…

asp.net core 跨域配置不起作用的原因

1、中间件配置跨域的顺序不对 中间件顺序配置对了基本上就能解决大部分问题中间件顺序配置对了基本上就能解决大部分问题 附上官网简单的启用跨域的代码 var MyAllowSpecificOrigins "_myAllowSpecificOrigins";var builder WebApplication.CreateBuilder(args);…

Linux 命令解释器-shell

概念 shell &#xff1a;壳&#xff0c;命令解释器&#xff0c;负责解析用户输入的命令 分类&#xff1a; 内置命令 (shell 内置 ) &#xff0c; shell 为了完成自我管理和基本的管理&#xff0c;不同的 shell 内置不同的命令&#xff0c;但是大 部分都差不多 外置命令&…

【开源免费】基于SpringBoot+Vue.JS网上超市系统(JAVA毕业设计)

本文项目编号 T 037 &#xff0c;文末自助获取源码 \color{red}{T037&#xff0c;文末自助获取源码} T037&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

apisix高性能网关实现一机一密

基于 APISIX 的一机一密实现方案 概述 基于 Apache APISIX 网关的一机一密实现方案&#xff0c;通过自主开发的自定义插件实现设备级别的密钥管理和加密通信。本方案通过扩展 APISIX 的插件机制&#xff0c;实现高可用、可扩展的 API 安全防护。 本方案的主要特点&#xff1a; …

嵌入式Linux的AXI平台(platform)驱动教程

本文以JFMQL100的Linux系统的AXI接口的平台驱动为例&#xff0c;介绍嵌入式Linux的平台驱动编写、测试软件编写以及验证方式。本文的方法适用于任意嵌入式芯片Linux的物理地址映射的平台&#xff08;platform&#xff09;驱动的编写、测试与应用。 本文中AXI的开始地址为0x8000…

Visual Studio Code(VSCode)中编写 TypeScript 代码

在 Visual Studio Code&#xff08;VSCode&#xff09;中编写 TypeScript 代码通常需要以下配置&#xff1a; 一、安装必要的扩展 TypeScript 插件&#xff1a;由微软官方提供&#xff0c;提供了语法高亮、错误检查、代码补全等功能。 二、配置 tsconfig.json 文件&#xff08;…

视频设备一体化监控运维方案

随着平安城市、雪亮工程等项目建设的号召&#xff0c;视频监控系统的建设如火如荼地开展。无论在公共场所、企业单位、住宅小区、矿山工地还是交通枢纽&#xff0c;视频监控系统已成为保障安全、维护秩序和提升管理效率的重要工具。但由于对视频监控系统中的前端设备&#xff0…

第十八章 Vue组件样式范围配置之scoped

目录 一、引言 二、案例演示 2.1. 工程结构图 2.2. 核心代码 2.2.1. main.js 2.2.2. App.vue 2.2.3. BaseOne.vue 2.2.4. BaseTwo.vue 2.3. 运行效果 2.4. 调整代码 2.4.1. BaseTwo.vue 2.4.2. 运行效果 三、scoped原理 一、引言 前面的几个章节在介绍组件的时…

可口可乐三季报 | 数字化助力,营收超预期 | ​eBest

可口可乐公司近日公布了2024年第三季度业绩报告。报告特别强调了数字技术&#xff0c;尤其是AI人工智能对推动增长的重要作用。 第三季度&#xff0c;可口可乐公司交出了一份亮眼的成绩单&#xff0c;营收和每股收益均超出市场预期&#xff0c;显示出公司业务的强大韧性和长期…

书生大模型实战营 L0 入门岛

书生大模型训练营入门岛任务——训练营链接 1. Linux前置知识 任务&#xff1a;端口转发 当使用vscode远程连接服务器时&#xff0c;在服务器运行的任务&#xff0c;vscode会自动帮忙进行端口映射&#xff0c;方便本地进行访问。 2. Python前置知识 任务1&#xff1a;Leec…

配置mysql 主主模式 GTID

文章目录 一、前提二、修改my.cnf主1 10.255.131.9主2 10.255.131.10 三、配置主主3.1 配置主 10.255.131.93.2 配置从 10.255.131.103.3 配置主 10.255.131.103.4 配置从 10.255.131.9 四、验证五、同步问题排查以及恢复5.1 查看同步状态5.2 查看同步是否数据一致性&#xff0…

【Vulnhub靶场】DC-5

DC-5靶机下载地址&#xff1a; https://download.vulnhub.com/dc/DC-5.zip 目标 本机IP&#xff1a;192.168.118.128 靶机IP&#xff1a;192.168.118.0/24 信息收集 扫描存活主机&#xff0c;扫描端口&#xff0c;扫描服务 第一步使用nmap扫描出目标IP为&#xff1a;192.168…