【入土级】详解C++类对象(中篇)

news2025/1/18 3:04:29

目录

前言:类的6个默认成员函数

一, 构造函数

1. 概念

2. 特性

二, 析构函数

2.1 概念

2.2 特性

2.3 牛刀小试 

三, 拷贝构造函数

3.1概念

3. 2 特点

四, 赋值运算符重载

4. 1 运算符重载 

五, const成员函数

六,取地址及const取地址操作符重载

七,练习——实现日期计算器

结语


前言:类的6个默认成员函数

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

 

一, 构造函数

 1. 概念

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

2. 特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务 并不是开空间创建对象,而是初始化对象
其特征如下:
  • 1. 函数名与类名相同
  • 2. 无返回值
  • 3. 对象实例化时编译器自动调用对应的构造函数。
  • 4. 构造函数可以重载。(后面拷贝构造函数会体现)
  • 5. 如果存在未自定义默认构造函数,编译器不再生成默认构造函数。 
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写,编译器默认生成的构造函数,都可以认为是默认构造函数。

下面是一段自定义构造函数的:

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 2023, int month = 5, int day = 9) // 自定义默认构造函数,设置全缺省参数,对数据进行初始化。
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

int main()
{
	Date z;
	Date z1(2012, 12, 12); // 由于我们自定义构造函数支持带参数设置数据初始化。
	// 接下来,我们注释掉自定义默认构造函数,来测试一下编译器默认自动生成的构造函数。
}

 当我们注释掉我们自定义的构造函数时,我们会发现z对象 的类成员变量,依旧是随机值。这里我们不禁会想,编译器自动生成的默认构造函数似乎什么也没做??

解答:C++把类型分成内置类型(基本类型)和自定义类型。
  • 内置类型就是语言提供的数据类型,如:int/char...;
  • 自定义类型就是我们使用class/struct/union等自己定义的类型。 
看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员 _t调用的它的默认成员函数。

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 2023, int month = 5, int day = 9) // 自定义默认构造函数,设置全缺省参数,对数据进行初始化。
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

class Time
{
public:
	Date _t;      // 自定义类型

private:          // 内置类型
	int _hour;
	int _minute;
	int _second;
};
int main()
{
	Time k;
}

调试结果如下: 

  特点:

1. 内置函数不做处理。

2. 自定义类型会调用(自定义类型的)默认构造函数。

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即: 内置类型成员变量在类中声明时可以给默认值

这里我们就有了两种内置成员初始化的方法: 

  • 1. 通过C++补丁初始化;()
  • 2. 自定义默认构造函数,同时给缺省参数。

二, 析构函数

2.1 概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而 对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

2.2 特性

析构函数是特殊的成员函数,其 特征如下:
  • 1. 析构函数名是在类名前加上字符 ~。
  • 2. 无参数无返回值类型。
  • 3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  • 4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

 首先我们来看下面代码:

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 2023, int month = 5, int day = 9)  // 默认构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

	~Date()   // 默认析构函数
	{
		_year = _month = _day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

我们将显式析构函数注释掉,让我们测试一下编译器自动生成的默认析构函数的结果,下面的程序我们会看到:编译器生成的默认析构函数,对自定类型成员调用它的析构函数。

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 2023, int month = 5, int day = 9)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	~Date()
	{ 
		cout << "~Date()" << endl;   // 对调用Date的析构函数进行标记
		_year = _month = _day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

class Time
{
public:
	Date _t;      // 自定义类型
	~Time()
	{
		cout << "~Time()" << endl; // 对调用Time的析构函数进行标记
	}
private:          // 内置类型
	int _hour = 10;
	int _minute = 10;
	int _second = 10;
};

void func()
{
	Time z;
}
int main()
{
	func();
	return 0;
}

 结果可以看出:

2.3 牛刀小试 

看下面代码,输出顺序是什么? 

class A
{
public:
	A(int a = 0)
	{
      _a = a;
		cout << "A(int a = 0)->" <<_a<< endl;
	}

	~A()
	{
		cout << "~A()->" <<_a<<endl;
	}
private:
	int _a;
};

A aa3(3);

void f()
{
	static int i = 0;
	static A aa0(0);
	A aa1(1);
	A aa2(2);
	static A aa4(4);
}

// 构造顺序:3 0 1 2 4 1 2
// 析构顺序:~2 ~1 ~2 ~1 ~4 ~0 ~3
int main()
{
	f();
	f();
	return 0;
}

核心思路:遵循栈的思路,先进后出

三, 拷贝构造函数

 3.1概念

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

那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?
拷贝构造函数只有单个形参,该形参是对本 类类型对象的引用(一般常用const修饰),在用 已存在的类类型对象创建新对象时由编译器自动调用。 

3. 2 特点

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

下面是会发生无穷递归代码:

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

	Date(Date b1)  // 正确代码:Date(Date& b1)
	{
		_year = b1._year;
		_month = b1._month;
		_day = b1._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

无穷递归如图:

 换成类类型引用即可解决无穷递归的问题。 

class Time
{
public:
	Time(int hour = 1 , int minute = 2, int second = 3)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}

	Time(const Time& a)
	{
		_hour = a._hour;
		_minute = a._minute;
		_second = a._second;
		cout << "Time(Time& a)" << endl;
	}

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

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

	Date(const Date& b1)  // 正确代码:Date(Date& b1)
	{
		_year = b1._year;
		_month = b1._month;
		_day = b1._day;
	}

	Time _t;
private:
	int _year;
	int _month;
	int _day;
};
void func()
{
	Date z;
	Date k(z);
}

int main()
{
	func();
	return 0;
}
在编译器生成的默认拷贝构造函数中,内置类型是按照 字节方式直接拷贝的,而自定义类型是调用其 拷贝构造函数完成拷贝的。

我们发现,编译器自动生成的拷贝构造函数,足够我们进行值拷贝,那么我们还需要自定义拷贝构造函数吗?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
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)
 {
 // CheckCapacity();
 _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;
}

所以类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,需要深度拷贝,则拷贝构造函数是一定要写的,否则就是浅拷贝

四, 赋值运算符重载

4. 1 运算符重载 

C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其 返回值类型与参数列表与普通的函数类似
函数名字为:关键字 operator后面接需要重载的运算符符号
函数原型: 返回值类型 operator操作符(参数列表)
注意:
  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
  • .*  ::   sizeof   ?:   . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。 

下面有有几个应用例子:

class Time
{
public:
	Time(int hour = 1 , int minute = 2, int second = 3)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}

	Time(const Time& a)
	{
		_hour = a._hour;
		_minute = a._minute;
		_second = a._second;
		cout << "Time(Time& a)" << endl;
	}

	bool operator==(const Time& a)   
	{
		return _hour == a._hour &&
			_minute == a._minute &&
			_second == a._second;
	}

	bool operator>(const Time& a)
	{
		return _hour > a._hour &&
			_minute > a._minute &&
			_second > a._second;
	}

   Time& operator=(const Time& a)   // 内成员赋值运算符重载
	{
		_hour = a._hour;
		_minute = a._minute;
		_second = a._second;
		return *this;
	}
	
    ~Time()
	{
		cout << "~Time()" << endl;
	}
private:        // 内置类型
	int _hour;
	int _minute;
	int _second;
};

这里对赋值运算符进行补充;

1.赋值运算符只能重载成类的成员函数不能重载成全局函数  

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故 赋值运算符重载只能是类的成员函数
2.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
既然 编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
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)
	{
		// CheckCapacity();
		_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;
	s2 = s1;
	return 0;
}

 结果分析:

所以,如果类中未涉及到资源管理,赋值运算符是否实现(浅拷贝)都可以;一旦涉及到资源管理则必须要实现(深拷贝)。

五, const成员函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数 隐含的this指针,表明在该成员函数中 不能对类的任何成员进行修改。

让我们看看下面的实例代码吧:

class moss
{
public:
	moss(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
	void Print() const  // 这就是const成员函数,同上面的Print函数是重载函数。
		// 试试将尾巴上的const 删去,看看会有什么事情发生。
	{
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
void Test3()
{
	moss d1(2022, 1, 13);
	d1.Print();
	const moss d2(2022, 1, 13);
	d2.Print();
}

 首先我们已经知道const具有限制权限的功能,比如 int this,如图:

 对本次事例解析:

六,取地址及const取地址操作符重载

这个比较容易理解,这两个默认成员函数一般不用重新定义 ,编译器默认会生成。 看下面代码:

class Date
{ 
public :
 Date* operator&()
 {
 return this ;
 }
 const Date* operator&()const
 {
 return this ;
 }
private :
 int _year ; // 年
 int _month ; // 月
 int _day ; // 日
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如 想让别人获取到指定的内容!

七,练习——实现日期计算器

用C++类编写一个日期计算器,利用今天的运算符重载知识,实现日期+天数,日期-天数,日期-日期的功能。

下面是代码分享:

头文件:

#pragma once

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

class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		// 因为需要频繁调用,所以写成内联函数。
		static int M[13] = { 0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int day = M[month];
		if (month == 2 &&
			((year % 4 == 0 && year % 100 != 0) ||
				(year % 400 == 0)))
		{
			day++;
		}
		return day;
	}
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;

		if (!CheckDate())
		{
			cout << "非法日期" << endl;
			exit(-1);
		}
	}

	bool CheckDate()
	{
		if (_month > 12 || _day > GetMonthDay(_year, _month))
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	// 拷贝构造函数
    // d2(d1)
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	// 赋值运算符重载
  // d2 = d3 -> d2.operator=(&d2, d3)
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
	// 析构函数, 全是内置函数,没什么好清理的,调用编译器自动生成的就行。
	~Date()
	{
		;
	}
		
	// 日期+=天数
	Date& operator+=(int day);
	// 日期+天数
	Date operator+(int day);
	// 日期-天数
	Date operator-(int day);
	// 日期-=天数
	Date& operator-=(int day);
	// 前置++
	Date& operator++();
	// 后置++
	Date operator++(int);
	// 后置--
	Date operator--(int);
	// 前置--
	Date& operator--();
	// >运算符重载
	bool operator>(const Date& d);
	// ==运算符重载
	bool operator==(const Date& d);
	// >=运算符重载
	bool operator >= (const Date& d);
	// <运算符重载
	bool operator < (const Date& d);
	// <=运算符重载
	bool operator <= (const Date& d);
	// !=运算符重载
	bool operator != (const Date& d);
	// 日期-日期 返回天数
	int operator-(const Date& d);

	int WeekDay()
	{
		Date start(1, 1, 1);
		int LeveDay = 1;
		int day = (*this - start);
		LeveDay += day;
		int WeekDay = LeveDay % 7;
		return WeekDay;
	}
	// 友元函数为外边全局函数拥有调用类成员的权限。
	friend ostream& operator<<(ostream& cou, const Date& a);
	friend istream& operator>>(istream& cin, Date& a);

	void Print()const  // 本身是 Date* const this--是不能修改其指向的this,
		//但可以修改其内容,为了能接受被const Date* const this的对象所以需要缩小权限(方法就是函数名后加const)
		//反正print函数没有修改功能。
	{
		cout << _year << endl;
		cout << _month << endl;
		cout << _day << endl;
	}

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

// 放在全局中可以外部调用重载后的运算符,但放在外面又不能访问类成员,这里会用到后面的友元函数。
// 重载cout输出流
inline ostream& operator<<(ostream& cou,  const Date& a)
{
	cou << a._year << "年" << a._month << "月" << a._day << "日";
	return cou;
}

// 重载cint提取流
inline istream& operator>>(istream& cin, Date& a)
{
	cin >> a._year >> a._month >> a._day;
	assert(a.CheckDate());
	return cin;
}

函数实现源文件:

#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"

// 日期+=天数
Date& Date::operator+=(int day)
{
	_day += day;
		while ( Date::GetMonthDay(_year, _month) < _day)
		{
			_day -= Date::GetMonthDay(_year, _month);
			_month++;

			if (_month == 13)
			{
				_month = 1;
				_year++;
			}
		}
	return *this;
}

// 日期+天数     让我们得出结果的临时拷贝,所以可以复用+= ,
// 但有一个缺点是,日期其实已经被污染,不能在原日期下重新调用。
Date Date::operator+(int day)
{
	*this += day;
	Date tmp = *this;
	return tmp;
}


// 日期-天数  //可以复用 -= ,但原日期被修改。
Date Date::operator-(int day)
{
	*this -= day;
	Date tmp = *this;
	return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;   // 如果_month == 0了,就提取不了对应月份的天数
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += Date::GetMonthDay(_year, _month);
	}
	return *this;
}

// 前置++  加完后返回
// 为了区分重载函数前置与后置++,C++做了重载区分,就是后置++增加一个int参数。
Date& Date::operator++()
{
    // 可以进行复用+=
	* this += 1;
	return *this;
}

// 后置++
Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}

// 后置--
Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}
// 前置--
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
// // >运算符重载
bool Date::operator>(const Date& d) 
{
	if ((_year > d._year) ||
		(_month > d._month) ||
		(_day > d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
	return _year == d._year &&
		_month == d._month &&
		_day == d._day;
}

// >=运算符重载
bool Date::operator >= (const Date& d)
{
	return (*this > d) || (*this == d);
}

// <运算符重载
bool Date::operator < (const Date& d)
{
	if ((_year < d._year) ||
		((_year <= d._year) && (_month < d._month)) ||
		((_year <= d._year) && (_month <= d._month) && _day < d._day))
	{
		return true;
	}
	else
	{
		return false;
	}
}
// <=运算符重载
bool Date::operator <= (const Date& d)
{
	return (*this < d) || (*this == d);
}
// !=运算符重载
bool Date::operator != (const Date& d)
{
	return _year != d._year &&
		_month != d._month &&
		_day != d._day;
}

// 日期-日期 返回天数  思路:用小的加到大的,计算中间的次数。
int Date::operator-(const Date& d)
{
	int flog = 1;
	Date a1(*this); // 假设a1大
	Date a2(d);
	if (a1 < a2)    // a1小替换为a2
	{
		a1 = d;
		a2 = *this;
		flog = -1;
	}
	int n = 0;
	while (a2 < a1)
	{
		/*if (a2._day == 9 && a2._month == 5)
		{
			int x = 0;
		}*/
		++a2;
		n++;
	}
	return n * flog;
}

测试源文件:

#define _CRT_SECURE_NO_WARNINGS 1

#include"Date.h"

void Test()
{
	const char* WeekRoom[7] = { "周天", "周一", "周二", "周三", "周四", "周五", "周六" };
   
	do
	{
	cout << "------------------------------------" << endl;
	cout << "--------请输入要操作的选项----------" << endl;
	cout << "----1. 日期+天数---2.日期-天数----" << endl;
	cout << "----3. 日期-日期---0.结束程序 --------" << endl;
	cout << "----4.  --------" << endl;
	int opertaton = 0;
	cin >> opertaton;
	Date a1, a2;
	int day;
	switch (opertaton)
	{
	case 1: 
		cout << "-----------"<< endl;
		cin >> a1 >> day;
		cout << (a1 += day) << endl;
		cout << WeekRoom[a1.WeekDay()] << endl;
		break;
	case 2:
		cout << "-----------" << endl;
		cin >> a1 >> day;
		cout << (a1 -= day) << endl;
		cout << WeekRoom[a1.WeekDay()] << endl;
		break;
	case 3:
		cout << "-----------" << endl;
		cin >> a1 >> a2;
		cout << (a1 - a2);
		break;
	case 0:
		cout << "结束程序";
		exit(-1);
	default:
		break;
	}
	} while (true);
}

int main()
{
	Test();
	return 0;
}

结语

本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论;如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。

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

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

相关文章

网站测试的主要方法

网站测试的主要方法 网站测试是保证网站质量的重要手段&#xff0c;通过对网站进行测试可以及时发现问题并修复&#xff0c;提高用户体验和网站的可靠性。本文将介绍网站测试的主要方法。 1.功能测试&#xff1a;测试网站的所有功能是否正常。通过模拟用户的操作&#xff0c;确…

最新水果FLStudio21中文版下载及快捷键操作教程

任何一款软件&#xff0c;其快捷键永远扮演着至关重要的作用&#xff0c;熟练运用快捷键不仅能够节省时间&#xff0c;提高工作效率&#xff0c;还有助于熟练掌握所使用的软件。作为一款功能强大的音乐编曲软件&#xff0c;FL Studio有着大量的快捷键&#xff0c;这些快捷键在一…

【ArcGIS Pro二次开发】(28):用地图斑导出用地用海汇总表

本工具的作用是将现状用地或规划用地导出Excel格式的用地用海汇总表。 实现这个功能的Arcpy脚本工具我之前已经做过&#xff0c;详见&#xff1a;ArcGisPro脚本工具【8】——用地图斑导出用地用海汇总表 这次试着在ArcGIS Pro SDK中来实现同样的功能。 一、要实现的功能 如上…

【存储数据恢复】H3C存储卷中的数据恢复案例

存储数据恢复环境&故障&#xff1a; H3C FlexStorage某型号存储&#xff0c;25块磁盘组建的RAID5&#xff0c;其中包含一块热备盘。 工作人员误操作将存储设备中原先的2个卷删除&#xff0c;删除之后又使用和删除2个卷同样大小的空间重建了一个卷。用户希望恢复删除的2个卷…

springboot缓存

1. 认识缓存 一级缓存 - 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质 - 使用缓存可以有效的减少低速数据读取过程的次数&#xff0c;提高系统性能 Service public class BookServiceImplCache implements BookService {Autowiredprivate BookDao book…

玩转服务器之环境篇:PHP和Python环境部署指南 | 京东云技术团队

前几篇文章中讲解了如何搭建docker和Java Web环境的方法&#xff0c;本篇文章来教大家搭建一个好的PHP和Python环境&#xff0c;可以帮助开发和运行PHP和Python应用程序&#xff0c;使其更加高效和稳定。 一、 PHP环境介绍 好的开发环境无疑会大大提升编码效率&#xff0c;近…

搭载RK3588的Orange Pi 5 Plus来了!首发“亲民价”649元起!

Orange Pi 5 Plus来了。令人惊艳的Orange Pi 5 家族将又添一个重量级新成员! 作为香橙派首款搭载瑞芯微RK3588的开发板&#xff0c;Orange Pi 5 Plus将成为顶级高性能开发板的臻选。先说几个令人惊艳的亮点&#xff1a;两个PCIe扩展的2.5G以太网接口&#xff1b;eMMC 闪存插座…

黑马Redis笔记高级篇 | 分布式缓存

黑马Redis笔记高级篇 | 分布式缓存 1、Redis持久化&#xff08;解决数据丢失&#xff09;1.1 RDB持久化1.1.1 定义1.1.2 异步持久化bgsave原理 1.2 AOF持久化1.3 RDB和AOF比较 2、Redis主从&#xff08;解决并发问题&#xff09;2.1 搭建主从架构2.2 主从数据同步原理2.2.1 全量…

这样连交换机和路由器,多少网工没试过?

大家好&#xff0c;我是老杨。 交换机已经说过很多遍了&#xff0c;园区组网案例、交换机级联、光模块配件解读等等…… 有群友说想要看更多的配置案例&#xff0c;这次就给你说说&#xff0c;交换机和路由器对接上网配置。 还搞不明白交换机和路由器的&#xff0c;复习一下…

webpack开发服务器配置

&#x1f482; 个人网站:[【紫陌】【笔记分享网】](http://zimo.aizhaiyu.com/) &#x1f485; 想寻找共同学习交流、共同成长的伙伴&#xff0c;[请点击【前端学习交流群】](http://zimo.aizhaiyu.com/wechat/wechat.html) 文章最后有作者l联系方式&#xff08;备注进群&am…

写给程序员Android Framework 开发,

前言 在 Android 开发者技能中&#xff0c;如果想进大厂&#xff0c;一般拥有较好的学历可能有优势一些。但是如果你靠硬实力也是有机会的&#xff0c;例如死磕Framework。Framework 知识广泛应用在Android各个领域中&#xff0c;重要性显而易见。 成为一名Android Framework…

【2023程序员必看】大数据行业分析

1、政策重点扶持&#xff0c;市场前景广阔 2014年&#xff0c;大数据首次写入政府工作报告&#xff0c;大数据逐渐成为各级政府关注的热点。 2015年9月&#xff0c;国务院发布《促进大数据发展的行动纲要》&#xff0c;大数据正式上升至国家战略层面&#xff0c;十九大报告提…

网络突发环路,原来可以这么解决啊

大家好&#xff0c;我是老杨。 我相信&#xff0c;任何一个网工都遇到过网络环路&#xff0c;遇到这个情况&#xff0c;你的应对方法是什么&#xff1f; 我了解到大部分的初阶网工&#xff0c;最开始都只能用拔插网线和重启观测法来排除回路。 简单来说&#xff0c;就是先给…

下一个超级生态节点openGauss ——鲲鹏开发者峰会2023 openGauss技术专题回顾

摘要 2023年5月6日&#xff0c;一场面向计算产业开发者的技术盛会“ 鲲鹏开发者峰会2023 ”在东莞松山湖正式拉开帷幕&#xff0c;在其中的“openGauss技术专场”上&#xff0c;openGauss相关专家和伙伴围绕openGauss社区进展、openGauss5.0版本技术创新&#xff0c;基于op…

前端开发之函数式编程实践 | 京东云技术团队

作者&#xff1a;京东科技 牛志伟 函数式编程简介 常见应用场景 1、ES6中的map、filter、reduce等函数 [1,2,3,4,5].map(x > x * 2).filter(x > x > 5).reduce((p,n) > p n);2、React类组件 -> 函数式组件hooks、Vue3中的组合式API 3、RxJS、Lodash和Ramd…

震惊!百度搜索地位崩塌,微软Bing和俄罗斯Yandex上升

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 中国的搜索引擎市场正在发生巨变。据美国一家网站流量检测机构StatCounte显示&#xff1a;从2022年到2023年&#xff0c;百度的搜索流量份额急剧下降至39.64%&#xff0c;而微软的Bing和俄罗斯的Ya…

Spring Boot集成ShardingSphere配合dynamic-datasource进行数据源切换 | Spring Cloud 49

一、前言 通过以下系列章节&#xff1a; Spring Boot集成ShardingSphere实现数据分片&#xff08;一&#xff09; | Spring Cloud 40 Spring Boot集成ShardingSphere实现数据分片&#xff08;二&#xff09; | Spring Cloud 41 Spring Boot集成ShardingSphere实现数据分片&…

沉痛悼念互联网[云原生领域]技术大牛----左耳朵耗子(陈皓老师)

陈皓老师&#xff0c;网名"左耳朵耗子"&#xff0c;是一位在互联网界享有盛誉的伟大人物。他的一生充满了创造力、智慧和奉献精神&#xff0c;对技术领域做出了卓越的贡献。 陈皓老师 为什么网名叫 左耳朵耗子? 陈皓老师之所以取名"左耳朵耗子"&#xff…

HDMI接口说明

目录 一、概述 二、信号说明 三、信号定义 四、握手过程 五、调试步骤 一、概述 HDMI ,High-Definition Multimedia Interface,即高清晰度多媒体接口&#xff0c;是电脑、电视等相关显示设备的接口。 二、信号说明 HDMI分为Source,Sink.Source即信号源&#xff0c;为HDMI…

网络基础协议

1、网络协议/TCP/UDP/HTTP 网络协议是指在计算机网络中&#xff0c;不同设备之间进行通信时所使用的规则、约定和标准。常见的网络协议包括TCP协议、UDP协议、HTTP协议等。 TCP协议是一种面向连接、可靠的传输层协议&#xff0c;它提供了可靠的数据传输和数据包检错能力。在网…