【C++初阶】类与对象(中)之你必须掌握的三个重要默认成员函数 --- 构造函数 +析构函数 + 拷贝构造函数

news2024/11/27 1:23:13

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


【本章内容】

前言

本章是补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的。 【上期地址】

目录

  • 前言
  • 一、介绍类的6个默认成员函数
  • 二、为什么会存在构造函数和析构函数
  • 三、构造函数
    • 3.1 什么是构造函数
    • 3.2 构造函数的特性
  • 四、析构函数
    • 4.1 什么是析构函数
    • 4.2 析构函数的特性
  • 五、总结构造函数和析构函数
  • 六、拷贝构造函数
    • 6.1 什么是拷贝构造函数
    • 6.2 拷贝构造函数的特性

一、介绍类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。 然而,空类中真的什么都没有吗?其实并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数(不写函数,编译器会自动生成函数)。 那么类中有哪些默认成员函数呢?让我们接着往下看

【空类】

//ClassName -- 类名
class ClassName 
{

};

在这里插入图片描述

二、为什么会存在构造函数和析构函数

在使用类的函数的时候,某些粗心的程序员可能会忘记初始化和销毁。特别是销毁,如果程序结束后没有及时销毁,可能会存在内存泄漏。为了能一劳永逸解决这个问题,C++就有了构造函数和析构函数,他可以自动完成初始化工作和销毁。

以下是部分栈的实现,在主函数中缺少栈的初始化和销毁

#include <iostream>
#include <stdlib.h>
using namespace std;

class Stack
{
public:
	// 栈的初始化
	void Init(int defaultCapacity = 4)
	{
		a = (int*)malloc(sizeof(int) * defaultCapacity);
		if (nullptr == a)
		{
			return;
		}
		capacity = defaultCapacity;
		top = 0;
	}
	
	// 入栈
	void Push(int x)
	{
		CheckCapacity();
		a[top] = x;
		top++;
	}

	// 出栈
	void Pop()
	{
		if (Empty())
			return;
		top--;
	}

	// 栈顶元素
	int Top()
	{
		return a[top - 1];
	}

	// 判断栈是否为空
	int Empty()
	{
		return top == 0;
	}

	// 栈的销毁
	void Destroy()
	{
		free(a);
		a = nullptr;
		top = capacity;
	}

	// 检查栈的容量
	void CheckCapacity()
	{
		if (top == capacity)
		{
			int newcapacity = capacity * 2;
			int* tmp = (int*)realloc(a, newcapacity * sizeof(int));
			if (tmp == nullptr)
			{
				return;
			}
			a = tmp;
			capacity = newcapacity;
		}
	}

private:
	int* a;
	int top;
	int capacity;
};

int main()
{
	Stack s1;
	//假设粗心的程序员未给栈初始化(已注释掉)
	//s1.Init();

	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);

	while (!s1.Empty())
	{
		printf("%d ", s1.Top());
		s1.Pop();
	}

	printf("\n");

	//假设粗心的程序员为进行栈的销毁(已注释掉)
	//s1.Destroy();

	return 0;
}

我们知道未给栈初始化会导致程序崩溃,未给栈销毁会导致内存泄漏。那么构造函数和析构函数又是如何帮助我们初始化和销毁的?以及它们的写法是怎么样的呢?让我们接着往下看

三、构造函数

3.1 什么是构造函数

构造函数是一个特殊的成员函数其名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在 对象整个生命周期内只调用一次。

3.2 构造函数的特性

构造函数虽然名称叫构造,但构造函数的主要任务:不是开空间创建对象,而是初始化对象

其特征如下:

  1. 函数名与类名相同。
  2. 无返回值。意思是:可以不用写返回类型
  3. 对象实例化时编译器自动调用对应的构造函数。

根据以上三个特征,我们可以轻松对以上部分栈进行修改

#include <iostream>
#include <stdlib.h>
using namespace std;

class Stack
{
public:
	//构造函数(初始化)
	Stack(int defaultCapacity = 4)
	{
		//其逻辑和栈的初始化一模一样
		a = (int*)malloc(sizeof(int) * defaultCapacity);
		if (nullptr == a)
		{
			return;
		}
		capacity = defaultCapacity;
		top = 0;

	}

	// 栈的初始化
	//有了构造函数以下代码可以屏蔽
	/*void Init(int defaultCapacity = 4)
	{
		a = (int*)malloc(sizeof(int) * defaultCapacity);
		if (nullptr == a)
		{
			return;
		}
		capacity = defaultCapacity;
		top = 0;
	}*/
	
	// 入栈
	void Push(int x)
	{
		CheckCapacity();
		a[top] = x;
		top++;
	}

	// 出栈
	void Pop()
	{
		if (Empty())
			return;
		top--;
	}

	// 栈顶元素
	int Top()
	{
		return a[top - 1];
	}

	// 判断栈是否为空
	int Empty()
	{
		return top == 0;
	}

	// 栈的销毁
	void Destroy()
	{
		free(a);
		a = nullptr;
		top = capacity;
	}

	// 检查栈的容量
	void CheckCapacity()
	{
		if (top == capacity)
		{
			int newcapacity = capacity * 2;
			int* tmp = (int*)realloc(a, newcapacity * sizeof(int));
			if (tmp == nullptr)
			{
				return;
			}
			a = tmp;
			capacity = newcapacity;
		}
	}

private:
	int* a;
	int top;
	int capacity;
};

int main()
{
	Stack s1;
	//假设粗心的程序员未给栈初始化(已注释掉)
	//s1.Init();

	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);

	while (!s1.Empty())
	{
		printf("%d ", s1.Top());
		s1.Pop();
	}

	printf("\n");

	//假设粗心的程序员为进行栈的销毁(已注释掉)
	//s1.Destroy();

	return 0;
}

【程序结果】

在这里插入图片描述

4. 构造函数支持函数重载

【例如】

class Date
{
public:
	// 1.无参构造函数
	Date()
	{

	}

	// 2.带参构造函数
	Date(int year, int month, int day)
	{
		Year = year;
		Month = month;
		Day = day;
	}

private:
	int Year;
	int Month;
	int Day;
};

int main()
{
	Date d1; // 调用无参构造函数
	Date d2(2015, 1, 1); // 调用带参的构造函数

	Date d3();//错误
	// warning: 未调用原型函数(是否是有意用变量定义的?)

	return 0;
}

【构造函数的调用】

  1. 构造函数的调用就是在对象后加上参数列表或者不加参数列表。如果要调用无参构造函数时,对象后面不用跟括号,否则编译器无法区分是否是对象还是函数名,如以上d3的错误
  2. 对于函数重载,避免产生调用歧义(例子配合下面第7点使用)。重载知识点详细请参考这篇博客–>点击跳转
  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数(不传参可以直接调用的),一旦用户显式定义编译器将不再生成
#include <iostream>
using namespace std;

class Date
{
public:
	void Print()
	{
		cout << Year << ' ' << Month << ' ' << Day << endl;
	}
private:
	//声明
	int Year;
	int Month;
	int Day;
};

int main()
{
	Date d1;
	d1.Print();

	return 0;
}

【笔记】
类中成员函数Print不是直接打印成员变量,注意:成员变量是声明。这里打印的是d1对象的Year、Month、Day ,原因是这里其实隐藏了一个this指针,在 C++ 中,其实有一个隐式指针叫this指针,它指向当前对象的地址。当一个对象调用它的成员函数时,编译器会隐式自动地将该对象的地址作为第一个参数传递给成员函数。因此,在一个类的成员函数内部,可以使用 this指针来访问该对象的成员变量和成员函数。
在这里插入图片描述
更多详细知识请查看这篇博客 --> 点击跳转

回归正传,我们继续观察其打印结果:

【运行结果】

在这里插入图片描述

上述类中并没有显式定义构造函数,因此编译器会自动生成一个无参的默认构造函数。但d1的成员变量通过编辑器初始化后打印的结果是一个随机的值,这是为什么呢?为什么不初始化为0呢?让我们接着往下看

  1. 不自己定义的构造函数情况下,编译器会生成默认的构造函数。但是看起来默认构造函数好像没什么用?原因是:上面d1对象调用了编译器生成的默认构造函数,使得d1对象的yearmonthday却是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?

解答:
在C++中,一般把类型分为两类:第一类是内置类型(基本类型),像intchardouble这些语言本身定义的类型就称为内置类型;第二种是自定义类型,就是自己定义的类型,诸如用struct、class、union等定义的类型。所以,若用户不自己定义构造函数,编译器默认的构造函数对内置类型不做初始化处理(注意:如果你的编译器对内置类型有初始化处理,这其实是编辑器的个性化行为,但我们还是要坚信编译器默认的构造函数对内置类型不做初始化处理),然而自定义类型会去调用编译器默认的构造函数
【总结】

  1. 当成员变量是内置类型的时候,最好自己写一个构造函数,不然如上所描述的,编译器默认的构造函数会对成员变量初始化成随机值;
  2. 若成员变量都是自定义类型,可以考虑使用编译器默认的构造函数。并且默认初始化都为0,当然也可以自己写一个构造函数,具体看要求

后来,C++11中针对编译器默认构造函数对内置类型成员不初始化的缺陷做出了优化,即:内置类型成员变量在类中声明时可以给缺省值(默认值)

#include <iostream>
using namespace std;

class Date
{
public:
	void Print()
	{
		cout << this->Year << ' ' << this->Month << ' ' << this->Day << endl;
	}
private:
	int Year = 2023;
	int Month = 5;
	int Day = 15;
};

int main()
{
	Date d1;
	d1.Print();

	return 0;
}

【程序结果】

在这里插入图片描述

再次提醒:以上的成员变量都是变量的声明,而不是初始化。这里给的是默认缺省值,供编译器生成默认构造函数使用

7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数

#include <iostream>
using namespace std;

class Date
{
public:
	// 1.无参的构造函数
	Date()
	{
		Year = 2023;
		Month = 5;
		Day = 15;
	}
	// 2.全缺省的构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		Year = year;
		Month = month;
		Day = day;
	}

	void Print()
	{
		cout << Year << "-" << Month << "-" << Day << endl;
	}
private:
	int Year;
	int Month;
	int Day;

};

int main()
{
	Date d1;
	d1.Print();

	return 0;
}

【错误报告】

在这里插入图片描述

四、析构函数

4.1 什么是析构函数

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

4.2 析构函数的特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数的函数名与类名也是相同,但是在类名前加上字符 ~
  2. 无参数无返回值类型。因此析构函数不能重载,所以一个类只能有一个析构函数
  3. 对象生命周期结束时,C++编译系统系统自动调用析构函数

根据以上三个特性,我们对一开始栈的代码末尾未初始化进行修改如下:

#include <iostream>
#include <stdlib.h>
using namespace std;

class Stack
{
public:
	//构造函数(初始化)
	Stack(int defaultCapacity = 4)
	{
		//其逻辑和栈的初始化一模一样
		a = (int*)malloc(sizeof(int) * defaultCapacity);
		if (nullptr == a)
		{
			return;
		}
		capacity = defaultCapacity;
		top = 0;

	}

	//析构函数(销毁)
	~Stack()
	{
		cout << "空间已被销毁" << endl;
		free(a);
		a = nullptr;
		top = capacity;
	}

	// 栈的初始化
	//有了构造函数以下代码可以屏蔽
	/*void Init(int defaultCapacity = 4)
	{
		a = (int*)malloc(sizeof(int) * defaultCapacity);
		if (nullptr == a)
		{
			return;
		}
		capacity = defaultCapacity;
		top = 0;
	}*/
	
	// 入栈
	void Push(int x)
	{
		CheckCapacity();
		a[top] = x;
		top++;
	}

	// 出栈
	void Pop()
	{
		if (Empty())
			return;
		top--;
	}

	// 栈顶元素
	int Top()
	{
		return a[top - 1];
	}

	// 判断栈是否为空
	int Empty()
	{
		return top == 0;
	}

	// 栈的销毁
	//void Destroy()
	//{
	//	free(a);
	//	a = nullptr;
	//	top = capacity;
	//}

	// 检查栈的容量
	void CheckCapacity()
	{
		if (top == capacity)
		{
			int newcapacity = capacity * 2;
			int* tmp = (int*)realloc(a, newcapacity * sizeof(int));
			if (tmp == nullptr)
			{
				return;
			}
			a = tmp;
			capacity = newcapacity;
		}
	}

private:
	int* a;
	int top;
	int capacity;
};

int main()
{
	Stack s1;
	//假设粗心的程序员未给栈初始化(已注释掉)
	//s1.Init();

	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);

	while (!s1.Empty())
	{
		printf("%d ", s1.Top());
		s1.Pop();
	}

	printf("\n");

	//假设粗心的程序员为进行栈的销毁(已注释掉)
	//s1.Destroy();

	return 0;
}

为了验证《对象生命周期结束时,C++编译系统系统自动调用析构函数》,我在析构函数加了【“空间已被销毁”】,最后让我们来看看结果:
在这里插入图片描述

  1. 若未显式定义析构函数,系统会自动生成默认的析构函数注意:系统自动生成默认的析构函数对内置类型成员不做处理,而自定义类型会去调用它的析构函数。 因此,什么时候写析构我做了一下总结:
    ①如果类中没有动态申请资源(堆区)时,析构函数就可以不写,直接使用编译器生成的默认析构函数就行。
    ②如果有动态申请资源,就需要显示写析构函数释放资源。
    ③其次,如果需要释放资源的成员都是自定义类型,不需要写析构。

在这里插入图片描述

五、总结构造函数和析构函数

构造函数和析构函数都是C++中的特殊函数。

  • 构造函数用于在创建对象时初始化对象的成员变量,它的名称与类名相同,没有返回类型,可以有参数。当对象被创建时,会自动调用构造函数。

  • 析构函数用于在对象被销毁时释放对象所占用的资源,它的名称也与类名相同,但在名称前面加上了一个波浪号(~),没有参数和返回值。当对象被销毁时,会自动调用析构函数。

  • 构造函数和析构函数都是可选的,如果不定义,编译器会自动生成默认的构造函数和析构函数。但在某些情况下,我们需要自定义构造函数和析构函数,以满足特定的需求,如初始化对象时需要进行一些特殊的操作,或者对象被销毁前需要释放一些资源。

总之,构造函数和析构函数是C++中非常重要的特殊函数,掌握它们的使用方法对于编写高质量的C++代码是至关重要的。

六、拷贝构造函数

6.1 什么是拷贝构造函数

只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

6.2 拷贝构造函数的特性

拷贝构造函数也是特殊的成员函数,其特征如下:

1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

根据以上特性,举一个拷贝构造函数的例子:

#include <iostream>
using namespace std;

class Date
{
public:
	// 构造函数(对d1初始化)
	Date(int year = 1, int month = 1, int day = 1)
	{
		Year = year;
		Month = month;
		Day = day;
	}

	// 拷贝构造函数
	//d是d1的别名
	Date(Date& d)
	{
		this->Year = d.Year;
		this->Month = d.Month;
		this->Day = d.Day;
	}

	void Print()
	{
		cout << this->Year << '-' << this->Month << '-' << this->Day << endl;
	}

private:
	int Year;
	int Month;
	int Day;
};

int main()
{
	Date d1(2024, 5, 15);
	d1.Print();

	Date d2(d1);
	d2.Print();

	return 0;
}

【程序结果】

在这里插入图片描述

这里的this指针就不再赘述了,在构造函数中还提了一嘴,详细内容还请看 —>[点击跳转]

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
  2. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?
    当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
  3. 拷贝构造函数典型调用场景:
    ①使用已存在对象创建新对象
    ②函数参数类型为类类型对象
    ③函数返回值类型为类类型对象

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

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

相关文章

chatgptH5源码开发

hatGPTH5源码开发需要以下步骤&#xff1a; 确定需求&#xff1a;在开发ChatGPTH5应用之前&#xff0c;需要明确用户的需求和目标&#xff0c;以便进行合理的设计和开发。 技术选型&#xff1a;选择适合的前端技术框架和工具&#xff0c;如React、Vue、Angular等&#…

Flink从入门到精通之-10容错机制

Flink从入门到精通之-10容错机制 流式数据连续不断地到来&#xff0c;无休无止&#xff1b;所以流处理程序也是持续运行的&#xff0c;并没有一个明确的结束退出时间。机器运行程序&#xff0c;996 起来当然比人要容易得多&#xff0c;不过希望“永远运行”也是不切实际的。因…

WritingGPT: 基于ChatGPT和AutoGPT打造个人写作团队

本文介绍了如何通过定义多个GPT角色&#xff0c;以自动化流水线的方式打造个人写作团队&#xff0c;让AI帮助我们完成文章写作、修改、SEO等所有流程。原文: How I Built WritingGPT, a Fully Automated AI Writing Team ChatGPT是AI内容创造领域的巨大飞跃&#xff0c;但当我们…

8 种「Python 程序」定时执行方式

在日常工作中&#xff0c;我们常常会用到需要周期性执行的任务&#xff0c;一种方式是采用 Linux 系统自带的 crond 结合命令行实现&#xff0c;另外一种方式是直接使用Python。 最近我整理了一下 Python 定时任务的实现方式&#xff0c;建议收藏后学习。 利用while True: sle…

pro如何添加定时任务

Pro v2.4版本开始后台可以开关控制定时任务&#xff0c;那如何添加新的定时任务呢&#xff1f; 第一步&#xff1a;设置定时任务名称及标识&#xff1b; 文件app\controller\admin\v1\system\SystemTimer中task_name()方法 /**定时任务名称及标识 * return mixed */ public fu…

25岁,本科学历,待业,如何成为优秀的数据分析师,值得关注!

25岁&#xff0c;本科学历&#xff0c;待业&#xff0c;如何成为优秀的数据分析师&#xff0c;值得关注&#xff01; 你是在工作几年后确定自己的职业方向的呢&#xff1f;还是一直都是处于迷茫&#xff0c;随波逐流的状态&#xff1f;都说谁的青春不迷茫&#xff0c;但时间是最…

服务限流方案总结

流控作用 一般的做后台服务的&#xff0c;都会接触到流控&#xff0c;一般的场景就是在流量异常&#xff0c;比如遭受攻击的时候&#xff0c;保障服务不过载&#xff0c;在可支持的范围内提供稳定的服务。比如我们的服务支持100QPS&#xff0c;当一下子来了1000个请求的时候&a…

全景 I 0基础学习VR全景制作,平台篇第22篇 热点功能—作品功能操作

本期为大家带来蛙色VR平台&#xff0c;热点功能—作品功能操作。 功能位置示意 热点&#xff0c;指在全景作品中添加各种类型图标的按钮&#xff0c;引导用户通过按钮产生更多的交互&#xff0c;增加用户的多元化体验。 作品热点&#xff0c;即全景项目跳转热点&#xff0c;可与…

您使用的ChatGPT错了!以下是如何领先于 99% 的 ChatGPT 用户

我们大多数人都错误地使用了 ChatGPT&#xff1a; 错误1&#xff1a;不在提示中包含示例。 错误2&#xff1a;忽略通过角色控制 ChatGPT 的行为。 错误3&#xff1a;让 ChatGPT 猜测内容&#xff0c;而不是向它提供一些信息。 通过3类有用的prompt工程掌握 ChatGPT的使用。 …

leetcode 1259. 不相交的握手

1259. 不相交的握手 提示 困难 33 company 字节跳动 company 苹果 Apple company 亚马逊 偶数 个人站成一个圆&#xff0c;总人数为 num_people 。每个人与除自己外的一个人握手&#xff0c;所以总共会有 num_people / 2 次握手。 将握手的人之间连线&#xff0c;请你返回连线…

十二、Feign客户端整合Hystrix服务保护

目录 1、项目pom文件中引入feign客户端依赖 2、编写feign客户端接口&#xff0c;并配置fallback回调方法的类 3、编写controller&#xff0c;使用feign客户端进行RPC远程过程调用 4、实现feign客户端接口方法&#xff0c;并实现feign客户端接口中的每个接口方法作为fallbac…

十一、Hystrix服务保护

目录 服务雪崩相关概念简述 服务的雪崩效应 造成服务雪崩的原因 服务雪崩最终的结果 防止服务雪崩的方法 一、服务降级 1、引入Hystrix 服务保护依赖 2、基于注解HystrixCommand使用Hystrix 2.1、在需要进行服务保护的方法上添加注解HystrixCommand&#xff0c;并指定…

公有云云硬盘(EBS)有效范围内扩容/存储规格变更指导手册

一、背景 某公有云环境中,云主机直连的云硬盘存储某数据库数据,随着数据的积累,大约10亿多条数据,云硬盘急需扩容,但前期规划云硬盘未开启lvm卷,且当前存储容量未达EBS容量限制,最大可达32T,因此觉得采用EBS规格变更的方式来实现主机存储的扩容; 二、注意点: 1)过…

Matlab入门教程|002球的体积问题

写给Matlab小白的教程。如果你已经安装了Matlab&#xff0c;手头有一堆Matlab教程&#xff0c;面对书中一堆术语和命令不知所措&#xff0c;那么&#xff0c;请看本教程&#xff0c;从零开始&#xff0c;快速上手。 1 本文要点 初等代数计算&#xff1a;求函数值&#xff0c;求…

算法小技巧:空间换时间,时间换空间?

前言&#xff1a;小细节&#xff0c;大道理&#xff0c;思路在前&#xff0c;代码在后。 名词解释&#xff1a; 算法效率&#xff1a;往往由时间效率和空间效率两个方面决定。 时间效率&#xff1a;时间效率被称为时间复杂度&#xff0c;指的是算法执行过程耗费的时间…

英文论文(sci)解读复现【NO.10】宁夏酿酒葡萄病虫害智能检测平台设计

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

龙蜥产品生态总监做客 InfoQ:后 CentOS 时代,国产操作系统能否扛起大旗?

随着 CentOS 全面停服即将进入尾声&#xff0c;企业选择一款既可保障系统稳定运行&#xff0c;又可提供专业技术支持的操作系统迁移显得尤为重要。那么&#xff0c;现存的 CentOS 以及衍生版用户或将面临哪些风险&#xff1f;一套完整的迁移方案应该包括哪些步骤&#xff1f;在…

Shap-E:3D资产的生成式AI大模型

OpenAI 刚刚发布了 Shap-E&#xff0c;这是一种基于文本提示和图像创建 3D 资产的生成模型&#xff0c;能够生成带纹理的网格和神经辐射场 &#xff0c;从而实现各种 3D 输出。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 在本教程中&#xff0c;我们将引导你在 Go…

【云原生进阶之PaaS中间件】第一章Redis-2.3.1主从复制部署模式

1 部署架构 Redis在日常部署的时候&#xff0c;可以有多种部署模式&#xff1a;单机、主从、哨兵、集群&#xff08;分区分片&#xff09;&#xff0c;因此本文将对上面这四种模式进行详细的讲解&#xff0c;特别是集群模式将进行最细致的讲解&#xff08;现行普遍使用的方式&a…

Cpolar内网穿透本地MariaDB数据库

Cpolar内网穿透本地MariaDB数据库 cpolar内网穿透本地MariaDB数据库&#xff0c;实现外公网环境下使用navicat图形化工具远程连接本地内网的MariaDB数据库 配置MariaDB数据库 安装MariaDB数据库 进入MariaDB数据库官网https://mariadb.com/downloads/community/,然后下载相应的…