【C++】—— 类和对象(中)

news2024/11/27 8:45:52

【C++】—— 类和对象(中)

文章目录

  • 【C++】—— 类和对象(中)
    • 前言
    • 1. 类的默认成员函数
    • 2. 构造函数
    • 3. 析构函数
    • 4. 拷贝构造函数
    • 5. 赋值运算符重载
      • 5.1 运算符重载
      • 5.2 赋值运算符重载
    • 结语

前言

小伙伴们大家好呀,昨天的 【C++】——类和对象(上) 大家理解的怎么样了

今天的内容难度会上升一大截,大家需要做好准备哦

1. 类的默认成员函数

默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。⼀个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后还会增加两个默认成员函数,移动构造和移动赋值,这个我们后面再讲解。默认成员函数很重要,也比较复杂,我们要从两个方面去学习:

  1. 我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求
  2. 编译器默认生成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

在这里插入图片描述

我们的学习重点是在前4个,即构造函数,析构函数,拷贝构造函数和赋值运算符重载

2. 构造函数

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

我们就拿之前写过的日期类来举个构造函数的例子

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

    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;// 日
};

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

构造函数的特征

  1. 函数名与类名相同
  2. 无返回值(即不需要写void)
  3. 对象实例化时编译器会自动调用对应的构造函数
  4. 构造函数可以重载可以写多个,即可以定义多种初始化方式

下面是构造函数的调用的几种方式

void TestDate()
{
Date d1; // 调用无参构造函数 (注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明)
         // Date d1();// error
Date d2(2024, 4, 10); // 调用带参的构造函数
}
  1. 之前说过,如果我们没有写,则C++编译器会自动生成一个默认构造函数(这个构造函数是不需要传参的),反之,如果我们写了,编译器将不再生成

但是,一般情况下我们最好自己去写这个构造函数,因为编译器自动生成的这个默认构造函数的初始化做的并不是很好,比如下面这个代码,我们可以看一下它的初始化:

class Date
{
public:
    // 如果我们显式定义了构造函数,编译器将不再生成
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    // 将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
    Date d1;// 但是没有初始化,成员变量都是随机值!!!

    return 0;
}

在这里插入图片描述

  1. 关于编译器生成的默认构造函数,我们可能会有疑惑,既然编译器自动生成的这个默认构造函数的初始化做的并不是很好,还是要自己去写这个构造函数,为什么会有这个默认构造函数呢,对象调用了编译器生成的默认构造函数,但是对象 _year/_month/_day ,依旧是随机值,也就说在这里编译器生成的默认构造函数并没有什么用

我们要知道的是,C++把类型分成内置类型和自定义类型

举个例子:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 内置类型
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

image-20241004145131878

我们发现编译器生成默认的构造函数:

  1. 对于内置类型成员变量,它没有处理(其实在C++标准中没有规定要不要做处理,所以有些编译器会处理,有些不会处理)

  2. 对于自定义类型成员变量才会调用他的默认构造函数

再来看看一个两个栈实现队列的例子:

#include<iostream>
using namespace std;

typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
	// ...

	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

// 两个Stack实现队列
class MyQueue
{
public:
	// 编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化
	~MyQueue()
	{
		cout << "~MyQueue()" << endl;
	}
private:
	Stack pushst;
	Stack popst;
};

int main()
{
	MyQueue mq;
	//Stack st1;
	//Stack st2;

	return 0;
}

编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化

大多数情况实现构造函数都需要我们自己实现(传参构造,内置类型的构造),少数情况类像MyQueue且Stack有默认构造可用和析构,可以不用写,但是构造函数大家应写尽写

这个构造函数不初始化的缺陷造成的种种复杂,所以在C++11 中,针对内置类型成员不初始化的缺陷,这里打了补丁

即:内置类型成员变量在类中声明时可以给默认值(缺省值)

还是举个例子:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 这里的内置类型成员变量在类中声明时我们自己给了缺省值
	int _year = 2024;
	int _month = 10;
	int _day = 4;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

在这里插入图片描述

  1. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个

注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数,我们可以看出,这些函数都是不传参就可以调用的

像这样:

class Date
{
public:
	// 1.无参构造函数
	/*Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}*/
  

	// 2.带参构造函数
	/*Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/

	// 3.全缺省构造函数
	/*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;
};

无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数,都叫做默认构造函数。但是这三个函数有且只有⼀个存在,不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫默认构造,实际上无参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调用的构造就叫默认构造

小结

  1. 一般情况下,构造函数都需要我们自己显式的去实现
  2. 只有少数的情况下可以让编译器自动生成构造函数(比如两个栈实现队列 MyQueue,成员全是自定义类型)

3. 析构函数

析构函数的定义:析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,比如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作。析构函数的功能类比我们之前Stack实现的Destroy功能,而像Date没有Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的

析构函数负责对象指向资源的清理,如果对象没有指向资源则不用写析构函数

栈帧销毁后类对象一同销毁,但其指针指向的资源没释放,会造成内存泄漏这时就需要析构函数在栈帧销毁前清理资源

析构函数的特征

  1. 析构函数名是在类名前加上字符 ~

  2. 无参数无返回值类型(这里的无参就印证的下面的析构函数不能重载的特性,而无返回值这点与上面的构造函数一致)

  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数,析构函数不能重载

  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

  5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?这里直接说结论,自动生成的析构函数做的事与构造函数类似:

    • 对于内置类型成员变量,它没有规定要不要处理
    • 对于自定义类型成员变量才会调用他的默认析构函数

举个例子:

class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 内置类型
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d;
	return 0;
}

代码运行结果如下:

在这里插入图片描述

那这个代码我们在Data里没有写析构函数,为什么会调用Time的析构呢

因为这里的 _year, _month和 _day 三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可,而 _t 是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数

  1. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类,有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类,跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型成员不做处理,其实也不用处理,因为栈帧销毁后会一起销毁。自定类型成员会调用他的析构函数

还是举一个两个栈实现队列的例子:

#include<iostream>
using namespace std;

typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
	// ...

	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

// 两个Stack实现队列
class MyQueue
{
public:
	// 编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化
	// 编译器默认生成MyQueue的析构函数调用了Stack的析构,释放的Stack内部的资源
	// 显示写析构,也会自动调用Stack的析构
	~MyQueue()
	{
		cout << "~MyQueue()" << endl;
	}
private:
	Stack pushst;
	Stack popst;
};

int main()
{
	MyQueue mq;
	//Stack st1;
	//Stack st2;

	return 0;
}
  • 自定义类型:没有写析构,生成默认析构,默认析构调用白定义类型的析构
  • 内置类型:写了析构调用显示的析构,没写生成默认析构,默认析构对自定义类型不做处理,函数栈帧结束后自动销毁

小结:

  1. 有资源需要显式清理,就需要写析构,如 Stack

  2. 有两种场景不需要显式写析构,默认生成即可

  • 没有资源需要清理,如:Date

  • 内置类型成员没有资源需要清理,剩下的都是自定义类型成员 ,如 : 两个栈实现队列MyQueue

4. 拷贝构造函数

拷贝构造函数的定义:如果⼀个构造函数的第⼀个参数是自⾝类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数

所以我们就可以采用拷贝构造函数的方式创建一个与已存在对象一某一样的新对象

拷贝构造函数的特征

  1. 拷贝构造函数是构造函数的一个重载形式(它是一种特殊的构造)
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,如果使用传值方式编译器直接报错,因为这会引发无穷递归调用
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//Date(const Date d) // 错误写法:编译报错,会引发无穷递归
	Date(const Date& d)  // 正确写法
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 10, 4);
	d1.Print();

	//下面两种写法是等价的
	Date d2(d1);//拷贝构造
	d2.Print();
	Date d3 = d1;//拷贝构造也可以这样写
	d3.Print();
}

为什么必须时拷贝构造函数的参数必须用引用呢?不用引用可以吗?

在这里插入图片描述

传值传参:C++规定传值传参必须调用拷贝构造,所以又会调用拷贝构造构造形参,又因为拷贝构造是传值传参这样就会一直调用拷贝构造去构造形参也就形成无限递归,所以传值传参拷贝构造又会调用拷贝构造就相当于递归,并且递归没有结束条件

传引用传参:因为是传引用传参就是给d取别名,所以没有形成拷贝构造也就不会引发无穷递归

正是因为在语法逻辑上这里会形成无穷递归,所以拷贝的参数必须带引用,我讲清楚了吗

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

举个例子:

class Time
{
public:
	Time()
	{
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	// 内置类型
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

int main()
{
	Date d1;
	Date d2(d1);// 因为Date类并没有显式定义拷贝构造函数,这里编译器会生成一个默认的拷贝构造函数

	return 0;
}

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

  1. 那就会有小伙伴问了,编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?

让我们来看下面的代码:

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
        _size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};

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

	Stack s2(s1);
    
	return 0;
}

代码运行结果如下:

在这里插入图片描述

哎,系统怎么崩掉了

这里的问题就与上面我们说的浅拷贝联系上了,因为它只有值拷贝,数组会指向跟之前一样的空间,所以最后它会析构两次导致崩溃

想要解决也挺简单的,就是要用深拷贝,深拷贝就是要开一个跟之前一样大的空间,有了两个空间,析构两次也就不会崩了

5. 赋值运算符重载

5.1 运算符重载

想要讲清楚赋值运算符重载,我们首先要了解 一下什么是运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似

函数命名:关键字 operator后面接需要重载的运算符符号

函数原型:返回值类型 operator操作符

需要注意的是

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. *(点星) , :: , sizeof ,?: ,. (星) 注意以上5个运算符不能重载

还是那我们熟知的日期类举例子:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator==(const Date& d2)// ==
	{
		return _year == d2._year
			&& _month == d2._month
			&& _day == d2._day;
	}

	bool operator>(const Date& d)// >
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year && _month > d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day > d._day)
		{
			return true;
		}
		return false;
	}

	bool operator >= (const Date& d)// >=
	{
		return *this > d || *this == d;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 5, 1);
	Date d2(2024, 4, 16);

	cout << (d1 == d2) << endl;
	cout << (d1 > d2) << endl;
	cout << (d1 >= d2) << endl;

	return 0;
}

代码运行结果如下:
在这里插入图片描述

在所有的比较大小的函数,我们已经实现的 == >>= , 像 < , <=!= 也差不多

现在我们就来思考一下,实现日期+天数呢,又该怎么实现呢

int GetMonthDay(int year, int month)//获取这个月的天数
{
    int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		return 29;
	}
	else
	{
		return monthDayArray[month];
	}
}

Date& operator+=(int day)
{
	_day += day;
	while (_day >= GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
}

这里我们思考一下这个代码能不能实现我们要求的逻辑

我们会发现我们是不是实现的 日期+=日期的逻辑,为什么这样说呢,我们要实现 日期+日期 的逻辑的话,this指针是不是不应该改变,所以我们就应该这样写

Date operator+(int day)
{
	Date tmp(*this);
	tmp += day;// 复用+= 
	return tmp;
}

实现 日期+日期 的逻辑就可以复用 += 的逻辑,使代码变得更简洁,更简单

5.2 赋值运算符重载

赋值运算符重载的定义:赋值运算符重载是⼀个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于⼀个对象拷贝初始化给另⼀个要创建的对象

在形式上,我们得先与第4点讲的拷贝构造做一个对比

int main()
{
	Date d1;
	Date d2(2024, 10, 4);

	Date d3(d2);// 拷贝构造(初始化) 

	d1 = d2;// 赋值重载(复制拷贝)    

	return 0;
}

通过上面的比较,我们不难看出,赋值重载和拷贝构造的区别

  • 赋值重载 : 已经存在两个对象之间拷贝
  • 拷贝构造 : 一个初始化另一个马上要创建的对象

举个例子:

Date& operator=(const Date& d)
{
	_year = d.year;
	_month = d.month;
	_day = d.day;
	return *this;
}

在这里插入图片描述

但是如果我们不写,用编译器默认的赋值运算符重载又会发生什么?

这里我们需要知道一个结论:如果类中未涉及到资源管理,赋值运算符是否实现都可以:(比如日期类),一旦涉及到资源管理则必须要实现(比如栈类),是不是与前面讲的默认成员函数很类似

小结:

  1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数
  2. 引用返回可以提高效率,有返回值目的是为了支持连续赋值场景
  3. 没有显式实现时,编译器会自动生成⼀个默认赋值运算符重载,默认赋值运算符重载行为跟默认拷贝函数类似,对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对定义类型成员变量会调用他的拷贝构造

结语

  • 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的赋值运算符重载就可以完成需要的拷贝,所以不需要我们显示实现赋值运算符重载
  • 像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器自动生成的赋值运算符重载完成的值拷贝/浅拷贝不符合我们的需求,所以需要我们自己实现深拷贝(对指向的资源也进行拷贝)
  • 像MyQueue这样的类型内部主要是自定义类型Stack成员,编译器自动生成的赋值运算符重载会调用Stack的赋值运算符重载,也不需要我们显示实现MyQueue的赋值运算符重载

这里还有一个小技巧,如果一个类显示实现了析构并释放资源,那么他就需要显示写赋值运算符重载,否则就不需要

这就是今天类和对象的内容,感觉是不是有些难的,所以大家就好好理解

好了,感谢你能看到这里,溜了溜了,我们下期再见吧

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

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

相关文章

【AI人工智能】文心智能体,双人冒险游戏智能体创作分享

背景 最近半年&#xff0c;“AI agent”&#xff08;智能体&#xff09;这一词汇变得非常热门。许多人以为创建自己的智能体会很复杂&#xff0c;实际上&#xff0c;现有的平台已经大大降低了操作门槛。只要有创意&#xff0c;几乎每个人都可以轻松创建属于自己的智能体。今天…

WordPress响应式Git主题响应式CMS主题模板

兼容 IE9、谷歌 Chrome 、火狐 Firefox 等主流浏览器 扁平化的设计加响应式布局&#xff0c;兼容电脑、和各个尺寸手机的完美响应 主题设置面板新增多种AD位&#xff0c;PC端和移动设备各不相同 在主题设置选项中就可以进行基本的SEO设置&#xff1a;首页、分类、文章等页面…

跟我学C++中级篇——函数调用的本质

一、进程的执行过程 正常的情况下&#xff0c;程序会被计算机从硬盘加载到内存中&#xff0c;然后跳转到主入口函数进行执行。依次按照逻辑对相关的模块进行加载调用。其中&#xff0c;最常用的就是调用一个函数&#xff0c;可以说&#xff0c;函数是C/C程序中的一个重要的基础…

Python——异常处理机制

Python 异常处理机制 Python异常与异常处理机制针对 Traceback 的解读try-except-else-finallyexcept语句except语句的机制在 except 语句中引用当前被处理的 Python 异常 finally语句finally语句执行后才能抛出未被处理的异常finally中执行return会导致异常丢失 raise 语句rai…

集合框架02:Collection使用(1)

视频链接&#xff1a;13.05 Collection使用&#xff08;1&#xff09;_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?p5&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 代码示例&#xff1a; package com.yundait.Demo01;import java.util.ArrayList; i…

hbuilderx+uniapp+Android健身房管理系统 微信小程序z488g

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 用户功能…

震撼!工业史上第一家万级别规模的工业数字化设备效果图平台

耗时八年打造&#xff0c;国内第一家万级别规模的工业数字化设备效果图平台 平台&#xff1a;www.kingview3d.cn 创作者&#xff1a;kingview3d郭工 行业&#xff1a;煤矿综合自动化、污水处理、净水处理、楼宇暖通、环保工程、医药废水处理、二供、无负压加压站、提升泵站、一…

模拟器GSN3之DHCP动态分配IP地址配置案例

前文《详解DHCP服务工作原理及配置案例》介绍了DHCP服务工作原理&#xff0c;要想彻底理解、应用DHCP服务&#xff0c;须通过实证案例学习&#xff0c;该文在GSN3虚拟环境下&#xff0c;构建DHCP服务的环境。 一、配置环境&#xff1a; 1、GSN3 2、路由器&#xff1a;R1、R2…

【微服务】服务注册与发现、分布式配置管理 - Consul(day5)

概述 作用 Consul的两大作用就是服务发现和注册与分布式配置管理。 服务发现在介绍Eureka组件的时候已经进行过详细概述&#xff0c;大概就是将硬编码到服务中的IP地址和端口号进行解耦&#xff0c;从而实现动态扩缩容、容错处理、服务管理等功能&#xff0c;通过服务注册和…

MAC备忘录空白解决方案

打开icloud->备忘录 取消勾选同步此MAC后再次勾选&#xff0c;然后点击完成即可。

<<迷雾>> 第7章 会变魔术的触发器(3)--R-S 触发器 示例电路

用来验证或非门反馈功能的完整电路 info::操作说明 如演示出现异常, 可点右侧面板的重置按钮重置 此处 R 和 S 都使用的是按钮开关 点击 R 可让 Q 熄灭 点击 S 可让 Q 亮起 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/cyjsjd…

针对线上消息积压的排查思路以及解决方案

一、背景 我们在日常工作中&#xff0c;经常会碰到线上告警&#xff0c;消息队列消息积压了&#xff0c;试想如果对消息的消费速率有要求的场景&#xff0c;消息积压一定会或多或少对自己本身的业务场景有影响&#xff0c;这里就针对消息积压的场景&#xff0c;谈谈具体的排查…

过滤器Filter【详解】

过滤器Filter 1、 现有问题 在以往的Servlet中&#xff0c;有冗余的代码&#xff0c;多个Servlet都有重复的代码 比如编码格式设置 登录信息认证 2、 概念 过滤器&#xff08;Filter&#xff09;是处于客户端与服务器目标资源之间的一道过滤技术。 过滤器 3、 过滤器作用 执…

Python办公自动化教程(006):Word添加标题

2.3 word标题 介绍&#xff1a; 在 python-docx 中&#xff0c;您可以使用 add_heading() 方法为文档添加标题。此方法允许您指定标题的文本和级别&#xff08;例如&#xff0c;一级标题、二级标题等&#xff09;。标题级别的范围是从 0 到 9&#xff0c;其中 0 表示文档的主标…

深度解析:从浏览器输入链接到页面展现的奇幻历程

〇、前言 当我们在浏览器中输入一个网址&#xff0c;例如&#xff1a;example.com&#xff0c;按下回车键后&#xff0c;会发生什么呢&#xff1f; 主要会发生以下这些过程&#xff1a;域名解析、建立HTTP连接、发送HTTP请求、数据传输、渲染网页、断开HTTP连接。 一、域名解…

类型转换【C++提升】(隐式转换、显式转换、自定义转换、转换构造函数、转换运算符重载......你想知道的全都有)

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; C系列语法知识_Stark、的博客-CSDN博客 座右铭&#xff1a;梦想是一盏明灯&#xff0c;照亮我们前行的路&#xff0c;无论风雨多大&#xff0c;我们都要坚持不懈。 一…

【srm系统】供应商管理,招投标管理,电子采购系统,询价管理

前言&#xff1a; 随着互联网和数字技术的不断发展&#xff0c;企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式&#xff0c;能够提高采购效率、降低采购成本、优化供应商合作效率&#xff0c;已成为企业实现效益提升的关键手段。系统获取在文末…

[含文档+PPT+源码等]精品基于Python实现的美术馆网站的设计与实现

基于Python实现的美术馆网站&#xff0c;其设计与实现背景主要源于以下几个方面的需求和发展趋势&#xff1a; 一、文化艺术领域的发展需求 随着文化娱乐活动的日益丰富&#xff0c;美术馆作为展示艺术作品、传播文化的重要场所&#xff0c;其管理和服务模式的创新对于提升公…

LabVIEW提高开发效率技巧----使用动态事件

在LabVIEW开发过程中&#xff0c;用户交互行为可能是多样且不可预知的。为应对这些变化&#xff0c;使用动态事件是一种有效的策略。本文将从多个角度详细介绍动态事件的概念及其在LabVIEW开发中的应用技巧&#xff0c;并结合实际案例&#xff0c;说明如何通过动态事件提高程序…

【售后资料】软件售后服务方案(word原件)

软件售后服务方案的售后服务范围广泛&#xff0c;涵盖了多个方面&#xff0c;以确保客户在使用软件过程中得到全面、及时的支持。具体来说&#xff0c;这些服务范围通常包括以下几个核心内容&#xff1a; 技术支持服务维护与更新服务培训与教育服务定制化服务数据管理与服务客户…