【C++】深度解析--拷贝构造函数(从0开始,详解浅拷贝到深拷贝,小白一看就懂!!!)

目录

一、前言

 二、拷贝构造函数

🍎概念解析

🥝特性解析

 💦为什么拷贝构造函数使用传值方式会引发无穷递归调用?

 💦为什么拷贝构造函数的形参中要加入 const 修饰

 💦若未显式定义,编译器会生成默认的拷贝构造函数吗?

 💦【浅拷贝】与【深拷贝】

 💦总结

🍇 产生拷贝构造的三种形式

1.当用类的对象去初始化同类的另一个对象时 

2.当函数的形参是类的对象,调用函数进行形参和实参结合时 

3.当函数的返回值是对象,函数执行完成返回调用者时

三、拷贝构造函数的总结

四、共勉 


一、前言

        在我们前面学习的中,我们会定义成员变量成员函数,这些我们自己定义的函数都是普通的成员函数,但是如若我们定义的类里什么也没有呢?是真的里面啥也没吗?如下:

class Date {};

        如果一个中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的任何一个类在我们不写的情况下,都会自动生成6个默认成员函数。
【默认成员函数概念】:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

 ⭐其中上次的博客已经详细的讲解了构造函数&&析构函数的使用方法,所以本次博客将继续深度的讲解拷贝构造函数

 二、拷贝构造函数

 🍎概念解析

在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎👫

 那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

 答案是,当然可以的啦,这也就牵扯出了我们所要学习的 -----------拷贝构造函数


 【拷贝构造函数概念】:只有单个形参该形参是对本 类 类型对象 的引用(一般常用const修饰),在用已存在的类 类型对象创建新对象时由编译器自动调用


 【代码举例】:日期类

class Date
{
public:
	Date(int year = 2024 ,int month = 3 ,int day = 13)    // 构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)      // 拷贝构造函数
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void print()
	{
		cout << "今天的日期是 :" << endl;
		cout << _year << '-' << _month << '-' << _day << endl;
	}
	~Date()                        // 析构函数
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

	// 创建一个与已存在对象一某一样的新对象
	Date d2(d1);   // 拷贝构造
	d2.print();
	return 0;
}

  【运行结果】:

🥝特性解析

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

1️⃣: 拷贝构造函数是构造函数的一个重载形式

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

3️⃣: 在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的

 💦为什么拷贝构造函数使用传值方式会引发无穷递归调用?

首先我们来看如下代码:

//全缺省构造函数
Date(int y = 2000, int m = 1, int d = 1)
{
	_year = y;
	_month = m;
	_day = d;
}
//拷贝构造函数
Date(Date d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}
int main(void)
{
	Date d1;
	Date d2(d1);	//调用形式
	return 0;
}
  • 上面这个Date(Date d)指的就是拷贝构造函数Date d2(d1);便是它的调用形式,用已经定义出来的对象 d1 来初始化 d2
  • 但是我们编译一下却看到报出了错误,说【Date类的复制构造函数不能带有Date类】,这是为什么呢?

  • 但此时若是我将形参的部分加上一个引用&就可以编过了,这是为什么呢?

可能上面的这种形式过于复杂了,我先用下面这两个函数调用的形式来进行讲解

  • 如果你看过我的C++引用详解这篇文章的话就可以知道对于Func1(d1)来说叫做【传值调用】,对于Func2(d2)来说叫做【传引用调用】

【注意】:

“值” 调用的时候, 形参是实参的拷贝,改变形参的值并不会影响外部实参的值。
         
“引用” 调用的时候,形参是实参的别名,共同拥有一个地址,改变形参的值,就相当于对实参本身进行操作。

两者之间的区别:“值” 调用  会比  传 “引用” 调用 中间多一步  拷贝的操作

如果对以上两个概念还不清楚的老铁可以去看看这两篇文章:
C语言的传值调用
C++的传引用调用

class Date
{
public:
	
	// 构造函数
	// 通常都会先 运行 构造函数 在运行 Init
	Date(int year = 2024,int month = 4,int day = 12)
	{
		_year = 2024;
		_month = 4;
		_day = 12;
	}
	//拷贝构造函数
	Date(Date& d)
	{
		cout << "调用拷贝构造" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		std::cout << "year:" << _year << std::endl;
		std::cout << "month:" << _year << std::endl;
		std::cout << "day:" << _year << std::endl;
	}
	// 析构函数
	~Date()
	{
		cout << "调用析构构造" << endl;
		cout << endl;
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 传值调用
void Func1(Date d1)
{
	cout << "Func1函数的调用" << endl;
}
// 传引用带哦用
void Func2(Date& d2)
{
	cout << "Func2函数的调用" << endl;
}

int main()
{
	Date d;

	Func1(d);
	Func2(d);
	return 0;
}

  • 通过运行上述代码观察可以发现,Func1传值调用,会去调用Date类的拷贝构造函数,然后再调用本函数;但是Func2传引用调用,却直接调用了本函数
  • 这就源于我们之前讲过的,对于【传值调用】会产生一个临时拷贝,所以此时d1d的拷贝;对于【传引用调用】不会产生拷贝,此时d2d的别名

        所以,从上述代码我们可以得出一个结论:在传值调用时,形参中有【自定义类型】会调用拷贝构造,在传引用调用时,形参中有【自定义类型】不会调用拷贝构造

  •  所以为什么说使用传值方式编译器直接报错,因为会引发无穷递归调用?
  •  对于 自定义类型(自己定义的类型) 的 传值调用 来说都会去调用拷贝构造,那此时我们转换回Date类的拷贝构造函数这里。通过下面的这张图其实你可以看出自定义类型的传值调用引发的递归问题是多么严重!

  •  通过Date d2(d1)需要实例化对象d2,所以要调用对应的构造函数,也就是拷贝构造函数,但是在调用拷贝构造函数之前要先传参,那刚才说了【自定义类型传参调用】就会引发拷贝构造,那调用拷贝构造就又需要传参数进来,传参数又会引发拷贝构造。。。于是就引发了这么一个无限递归的问题
  •  所以编译器就规定了对于拷贝构造这一块的参数不可以是【传值传参】,而要写成下面这种【传引用传参】的形式。此时d就是d1的别名,那因为是d2去调用的拷贝构造,此时this指针所接收的便是d2的地址,初始化的即为d2的成员变量

 所以 在写 拷贝构造的时候  单个的形参,必须是对本类 类型对象的引用(&)。

 正确写法:

Date(Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

Date d2(d1);

 💦为什么拷贝构造函数的形参中要加入 const 修饰

 面这种拷贝构造的形式并不是很规范,一般的拷贝构造函数都写成下面这种形式

Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}
  • 那此时就会同学很疑惑,为什么要在前面加上一个const呢?这一点其实我们在strcpy中其实也有说到过,有的时候你可能会不小心把代码写成下面这样👇
Date(Date& d)
{
    // 写反了
	d._year = _year;
	d._month = _month;
	d._day = _day;
}
  • 但若是将const加上后,编译器便报出了错误❌

  • 因此可以看到,加上这个const之后,程序的安全性就得到了提升,这就是它的第一个作用①

 它还有第二点作用,我们再来看看

  • 我在实例化这个d1对象的时候在前面加上了一个const,此时这个对象就具有常属性,不可以被修改,然后此时再去使用d1对象初始化d2对象会发生什么呢?
int main(void)
{
	const Date d1;
	Date d2(d1);

	return 0;
}
  • 可以看到,编译器报出了错误,说【没有匹配的构造函数】,其实这里真正的问题还是在于权限放大,这点我在C++引用中也重点讲解过,如果不懂的同学去看一看。
  •   本来这个d1对象被const所修饰具有常性,但是呢在将其当做参数传入给一个不具有常性的对象接收时,那么在拷贝构造函数内部便可以去修改这个对象的内容,也就造成了问题。不要以为这种问题不会发生,我们在写程序的时候一定要严谨,尽可能地考虑到多种情况

  • 但是给形参加上const做修饰之后,便可以做到【权限保持】,此时程序的安全性又增加了↑

小结一下,对于const Date& d这种不是做输出型参数,加上前面的const的好处在于

① 防止误操作将原对象内容修改
② 防止传入const对象造成【权限放大】

💦若未显式定义,编译器会生成默认的拷贝构造函数吗?

 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

  • 此时我将上面所写的拷贝构造去除之后,再去进行一个拷贝的操作,通过下面的运行结果可以看出,d1和d2均完成了初始化操作,而且和构造函数一样,对于内置类型也会去进行处理。其实在这里就是调用了编译器默认为我们生成的拷贝构造
//以下为有参构造
Date(int year = 2000, int month = 1, int day = 1)
{
	_year = year;
	_month = month;
	_day = day;
}

内置类型会处理,那自定义类型呢?也会处理吗?

  • 此时我在Date类中声明了一个Time类的对象作为成员函数,并且去除了Date类中上面所写的【拷贝构造函数】,然后再用d1去初始化d2,你认为此刻会发生什么呢?
class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	//构造..
	//析构
private:
	int _year;
	int _month;
	int _day;
	
	Time _t;	//内置自定义类型的成员
};
  • 通过调试观察可以发现,即使是Date类中没有写拷贝构造函数d2依旧是完成了初始化工作.这个Time类我们在说析构函数的时候有讲到过,那此时要去析构Date类中的自定义类型成员_t,便要调用Time类的析构函数,但是要先调用编译器为Date类自动生成的析构函数,然后再去调用Time类的析构函数,此时自动生成的析构函数就派上了用场【忘记了再翻上去看看】
  • 既然构造、析构都可以自动生成,那么拷贝构造作为类的默认成员函数编译器也是会自动为我们生成。那么此时就会调用默认生成的拷贝构造去拷贝其内部自定义类型_t的时候就会去调用Time类的显式拷贝构造完成初始化工作 

 因此对于像Date这种日期类来说,我们可以不用去自己去实现拷贝构造,编译器自动生成的就够用了,那其他类呢,像Stack这样的,我们继续来看看

 💦【浅拷贝】与【深拷贝】

  • 继续延用我们上面所讲到过的Stack,而且没有写上拷贝构造函数,首先实例化出对象st1,接下去便通过st1去初始化st2,通过上面的学习可以知道会去调用编译器自动生成的【拷贝构造】来完成,不过真的可以完成吗?我们来运行一下试试💻
typedef int DataType;
class Stack
{
public:
	// 构造函数 
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	 拷贝构造 
	//Stack(const Stack& st)
	//{
	//	//根据st的容量大小在堆区开辟出一块相同大小的空间
	//	_array = (DataType*)malloc(sizeof(DataType) * st._capacity);
	//	if (nullptr == _array)
	//	{
	//		perror("fail malloc");
	//		exit(-1);
	//	}

	//	memcpy(_array, st._array, sizeof(DataType) * st._size);		//将栈中的内容按字节一一拷贝过去
	//	_size = st._size;
	//	_capacity = st._capacity;
	//}

	void Push(const DataType& data)
	{
		// 扩容...
		_array[_size] = data;
		++_size;
	}

	DataType Top()
	{
		return _array[_size - 1];
	}
	// 析构函数
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _capacity;
	size_t _size;
};

int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
    st1.Push(4);

	Stack st2(st1);
	return 0;
}

 运行结果出现报错:

  • 其实,根本的原因就是在于我们要使用到数组栈,便要去内存中开辟一块空间,那么s1开辟了一块空间后_array就指向堆中的这块内存地址,接着s2去拷贝了s1,里面的数据是都拷贝过来了,但是s2的_array也指向了堆中的这块空间

 s1 和 s2 指向了同一个空间,这是错误的。

  • 那此时我去往s1里面push数据的之后,s2再去push,就会造成【数据覆盖的情况】。假设现在s1push了【1】、【2】、【3】,那么它的size就是3,但是s1与s2二者的size是独立的,不会影响,所以此时s2的size还是0,再去push【4】、【5】、【6】的话还是会从0的位置开始插入,也这就造成了覆盖的情况

不仅如此,二者指向同一块数据空间还会造成其他的问题 

  • 现在定义出来两个Stack对象,那此时我想问谁会先去进行析构呢?

  • 揭晓一下,s2会先去析构,在C/C++内存分布一文中我们有讲到过【栈区】是里面的一个区域,原理都清楚是先进后出的,所以后实例化出的对象s2会先去进行一个析构的操作,接着再去析构对象s1。不过呢通过调试可以观察到s1和s2的_array都指向堆中的同一块空间,因此当s2去调用析构函数释放了这块空间后,那么s1对象的_array就已经是一个野指针了,指向了堆中的一块随机地址,那再去对这块空间进行析构的话就会出现问题⚠

👉所以来总结一下指向同一块空间的问题 

  1. 插入删除数据会互相影响
  2. 析构两次会造成程序的奔溃

 那要如何去解决这个问题呢?此时就要涉及到【深拷贝】了

 💬调用编译器自动为我们生成的拷贝构造函数去进行拷贝的时候会造成【浅拷贝】的问题,那什么又叫做深拷贝呢?

  •  因为浅拷贝是原封不动地拷贝,会使得两个指针指向同一块空间,那若是我们再去自己申请一块空间来使用,让两个对象具有不同的空间,此时便不会造成上面的问题了

 接下去我就来实现一下如何去进行【深拷贝】

Stack(const Stack& st)
{
	//根据st的容量大小在堆区开辟出一块相同大小的空间
	_array = (DataType *)malloc(sizeof(DataType) * st._capacity);
	if (nullptr == _array)
	{
		perror("fail malloc");
		exit(-1);
	}

	memcpy(_array, st._array, sizeof(DataType) * st._size);		//将栈中的内容按字节一一拷贝过去
	_size = st._size;
	_capacity = st._capacity;
}

  • 而且两块空间是独立的,所以在对象进行析构的时候也不会造成二次析构的问题

 💬 但是这样自己去写拷贝构造感觉很麻烦诶,哪些类需要这样去深拷贝呢?

  • 你可以观察在当前这这个类中是否存在显式的析构函数,若是存在的话,表示当前这个类涉及资源管理了【资源管理指得就是去堆中申请空间了】,此时你一定要自己去是实现拷贝构造以达到一个深拷贝;若是不涉及资源管理的话,直接使用编译器自动生成的进行浅拷贝就可以了
  •  像Date日期类这种只存在【年】、【月】、【日】这种内置类型的浅拷贝就可以了;像是复杂一些的,例如:链表、二叉树、哈希表这些都会涉及资源的管理,就要考虑到深拷贝了

经过上面的【浅拷贝】与 【深拷贝】 的深度解析,我们再来写一个字符串类,练练手

class MyString {
public:
	// 默认构造函数
	MyString(const char* str = "winter")
	{
		_str = (char*)malloc(sizeof(char) * (strlen(str) + 1));
		if (_str == nullptr)
		{
			perror("malloc fail!");
			exit(-1);
		}
		memcpy(_str, str, sizeof(char) * (strlen(str) + 1));
	}

	// 析构函数
	~MyString()
	{
		cout << "~String()" << endl;
		free(_str);
	}

	void MyPrintf()
	{
		cout << _str << endl;
		//printf("%s\n", _str);
	}

private:
	char* _str;
};

int main()
{
	MyString s1("hello C++");
	MyString s2(s1);
	s1.MyPrintf();
	cout << endl;
	s2.MyPrintf();
	cout << endl;
}

 如图:指向了同一块空间

那么会引发什么问题呢?会导致 _str 指向的空间被释放两次,引发程序崩溃。

加入深入拷贝构造函数: 

// 拷贝构造函数
	MyString(const MyString& s)
	{

		 //给新对象申请一段和原对象一样大小的空间
		_str = (char*)malloc(sizeof(char) * (strlen(s._str) + 1));
		if (_str == nullptr)
		{
			perror("malloc fail!");
			exit(-1);
		}

		 //把原对象的数据一一拷贝给新对象
		memcpy(_str, s._str, sizeof(char) * (strlen(s._str) + 1));
	}

 💦总结

⭐总结: 

1️⃣:你可以观察在当前这这个类中是否存在显式的析构函数,若是存在的话,表示当前这个类涉及资源管理了【资源管理指得就是去堆中申请空间了】,此时你一定要自己去是实现拷贝构造以达到一个深拷贝;若是不涉及资源管理的话,直接使用编译器自动生成的进行浅拷贝就可以了

2️⃣: 像Date日期类这种只存在【年】、【月】、【日】这种内置类型的浅拷贝就可以了;像是复杂一些的,例如:链表、二叉树、哈希表这些都会涉及资源的管理,就要考虑到深拷贝了

🍇 产生拷贝构造的三种形式

深刻理解了拷贝构造之后,我们再来看看产生拷贝构造的三种形式 

1.当用类的对象去初始化同类的另一个对象时 

Date d1;
Date d2(d1);
Date d3 = d2;	//也会调用拷贝构造

 在实例化对象d2和d3的时候都去调用了拷贝构造,最后它们初始化后的结果都是一样的

2.当函数的形参是类的对象,调用函数进行形参和实参结合时 

void func(Date d)	//形参是类的对象
{
	d.Print();
}
 
int main(void)
{
	Date d1;
	func(d1);	//传参引发拷贝构造
	
	return 0;
}

函数func()的形参是类的对象,此时在外界调用这个函数并传入对应的参数时,就会引发拷贝构造, 

3.当函数的返回值是对象,函数执行完成返回调用者时

Date func2()
{
	Date d(2023, 3, 24);
	return d;
}
 
int main(void)
{
	Date d1 = func2();
	d1.Print();
 
	return 0;
}

 可以看到,这一种方式也会引发拷贝构造,当函数内部返回一个Date类的对象时,此时外界再使用Date类型的对象去接收时,就会引发拷贝构造。 

三、拷贝构造函数的总结

 ✨总结:

1. 拷贝构造算是六大默认成员函数中较难理解的了。主要就是要理清【内置类型】和【自定义类型】是否会调用拷贝构造的机制。还有在实现这个拷贝构造时要主要的两点:一个就是在形参部分要进行引用接收,否则会造成无穷递归的现象;还有一点就是在前面加上const进行修饰,可以防止误操作和权限放大的问题
2. 一般的类,自己生成拷贝构造就够用了,只有像Stack这样自己直接管理资源的类,需要自己实现深拷贝。

四、共勉 

以下就是我对 拷贝构造函数 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++的理解请持续关注我哦!!!  

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

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

相关文章

使用阿里云试用Elasticsearch学习:4. 聚合——2

近似聚合 如果所有的数据都在一台机器上&#xff0c;那么生活会容易许多。 CS201 课上教的经典算法就足够应付这些问题。如果所有的数据都在一台机器上&#xff0c;那么也就不需要像 Elasticsearch 这样的分布式软件了。不过一旦我们开始分布式存储数据&#xff0c;就需要小心…

干货 | 百亿节点,毫秒级延迟,基于nebula的大规模图应用实践

背景 2017年9月携程金融成立&#xff0c;在金融和风控业务中&#xff0c;有多种场景需要对图关系网络进行分析和实时查询&#xff0c;传统关系型数据库难以保证此类场景下的关联性能&#xff0c;且实现复杂性高&#xff0c;离线关联耗时过长&#xff0c;因此对图数据库的需求日…

人工智能科普:人工智能的分类

人工智能的分类多种多样&#xff0c;根据不同的标准和应用场景&#xff0c;可以将其划分为多个不同的类别。以下是对人工智能分类的详细探讨。 一、按应用领域分类 1. 智能机器人&#xff1a;智能机器人是人工智能技术在机器人领域的应用。它们能够根据环境和任务的不同进行自…

视觉SLAM学习打卡【10】-后端·滑动窗口法位姿图

本节是对上一节BA的进一步简化&#xff0c;旨在提高优化实时性.难点在于位姿图部分的雅可比矩阵求解&#xff08;涉及李代数扰动模型求导&#xff09;&#xff0c;书中的相关推导存在跳步&#xff08;可能数学功底强的人认为过渡的理所当然&#xff09;&#xff0c;笔者参考了知…

ELK企业级日志分析系统以及多种部署

目录 ELK简介 ELK简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 ●ElasticSearch&#xff1a;是基于Lucene&#xff08;一个全文检索引…

Command开源AI的未来

在AI的浩瀚宇宙中&#xff0c;有一个新星正在闪耀——Command R。这个开源的大型语言模型不仅在技术排行榜上名列前茅&#xff0c;更以其开放性和高性能赢得了全球开发者的关注和喜爱。 开源精神的胜利 Command R是由CohereAI推出的一款开源大语言模型&#xff0c;拥有1040亿…

CTFshow电子取证——内存取证2

接上回 JiaJia-CP-2 2.佳佳在网页上登录了自己的邮箱&#xff0c;请问佳佳的邮箱是&#xff1f; 因为是在网页上登陆的邮箱 用iehistory插件 查看一下网页历史记录 为了方便分析&#xff0c;使用grep命令正则匹配一下 **com 的记录 vol.py -f JiaJia_Co.raw --profileWin…

线程池-异步编排-完成时回调-线程串行化

上图中用exceptionally可以感知异常也可以处理返回结果 同时 我们使用handle也可以做到这种情况 线程串行化

IDEA Warnings:SQL dialect is not configured.

springboot项目XxxMapper.xml文件打开后显示warnings&#xff1a;SQL dialect is not configured......&#xff08;翻译&#xff1a;未配置SQL语言。&#xff09; 大概意思是没有在IDEA中配置当前sql是MySQl、Oracle还是MariaDB等语言。 配置一下就好&#xff1a; 完了&#…

synchronized底层原理

1synchronized关键字的底层原理 Monitor 举个例子&#xff1a; 1.线程1执行synchronized代码块&#xff0c;里面用到了lock(对象锁)。首先会让这个lock对象和monitor关联&#xff0c;判断monitor中的owner属性是否为null。如果为null直接获取对象锁。owner只能关联一个线程。 2…

node.js学习笔记(一):什么是node.js、fs 文件系统模块、path 路径模块、综合案例 - 时钟案例

目录 一、初识Node.js 1. 1.什么是 Node.js 1.2.Node.js 中的 JavaScript 运行环境 1.3. Node.js 可以做什么 1.4. Node.js 环境的安装 1.5. 在 Node.js 环境中执行 JavaScript 代码 二、fs 文件系统模块 2.1 什么是 fs 文件系统模块 2.2 读取指定文件中的内容 2.3 向…

牛顿-欧拉递推动力学方程①

文章目录 力和力矩的递推算式1 第i个连杆的静力平衡方程2 第i个连杆的动力平衡方程(不计重力)牛顿—欧拉递推动力学算法向外递推计算连杆的速度和加速度向内递推计算力和力矩计及重力的牛顿—欧拉动力学算法牛顿—欧拉动力学方程是应用达朗伯原理将动力学问题转化为牛顿—欧拉…

解决vue3更新chunk包后,点击页面报错

出现错误 解决思路 试了好多方法&#xff0c;跳了很多坑&#xff0c;router版本对不上&#xff0c;解决方案不实用。最后我直接捕获异常&#xff0c;刷新页面&#xff0c;解决最快最有效。 // vue-rotuer版本 "vue-router": "^4.0.3"解决方案 在router/…

Java基础-知识点03(面试|学习)

Java基础-知识点03 String类String类的作用及特性String不可以改变的原因及好处String、StringBuilder、StringBuffer的区别String中的replace和replaceAll的区别字符串拼接使用还是使用StringbuilderString中的equal()与Object方法中equals()区别String a new String("a…

springboot+ssm+java医生绩效管理系统

框架&#xff1a;SSM/springboot都有 jdk版本&#xff1a;1.8 及以上 ide工具&#xff1a;IDEA 或者eclipse 数据库: mysql 编程语言: java 前端&#xff1a;layuibootstrapjsp 详细技术&#xff1a;HTMLCSSJSjspspringmvcmybatisMYSQLMAVENtomcat 开发工具 IntelliJ IDEA: …

lanqiao.602 迷宫

题目&#xff1a; 代码&#xff1a; #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; char mp[31][51]; //稍微开大一点 char k[4]{D,L,R,U}; //按字典序记录路径 int dirx[]{1,0,0,-1},d…

数模 初见数建

文章目录 初见数学建模1.1 数学建模是什么1.2 数学建模的概述1.3 如何学习数学建模---分模块化1.4 数学建模前提了解1.5 数学建模的六个步骤1.6 如何备战建模比赛1.7 数学建模赛题类型1.8 数学建模算法体系概述 初见数学建模 1.1 数学建模是什么 1.原型与模型 原型&#xff…

虚幻引擎架构自动化及蓝图编辑器高级开发进修班

课程名称&#xff1a;虚幻引擎架构自动化及蓝图编辑器高级开发进修班 课程介绍 大家好 我们即将推出一套课程 自动化系统开发。 自动化技术在项目开发的前中后期都大量运用。如何您是一家游戏公司&#xff0c;做的是网络游戏&#xff0c;是不是经常会遇到程序员打包加部署需…

RedisTemplate对象中方法的使用

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Redis是一个key-va…

【Java探索之旅】方法重载 递归

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、方法重载1.1 为什么要有方法重载1.2 方法重载的概念与使用1.3 方法签名 二、递归2…