C++—类与对象(中)

news2025/4/20 14:11:45

目录

1、类的6个默认成员函数

2、构造函数

构造函数的特性

3、初始化列表

4、析构函数

概念

5、拷贝构造函数

6、运算符重载

7、赋值运算符重载

赋值运算符重载格式

8、前置++和后置++重载

9、const修饰的成员

10、取地址及const取地址重载


1、类的6个默认成员函数

一个类中没有成员,称为空类。

任何类在什么都不写时,编译器会自动生成六个成员函数,分别是

构造、析构、拷贝构造、赋值重载、取地址重载和const对象取地址重载

2、构造函数

先来看一下这个日期类

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_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.Init(2025, 2, 28);
	d1.Print();
}

我们可以看到当我们想要初始化一个结构体对象时都要手动调用初始化函数,而构造函数能帮助我们每次创建变量都能自动调用初始化函数。

构造函数是一个特殊的成员函数,没有返回值(连空都不是),函数名和类名相同,创建对象时编译器可以自动调用这个函数,对单个对象来说,在这个对象的整个生命周期只会调用一次。

构造函数的特性

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

特性:

1、函数名和类名相同
2、无返回值(就是真正的没有,连空都不是)
3、对象实例化时由编译器自行去调用构造函数
4、构造函数可以重载

构造函数可以重载

class Date
{
public:
	//无参构造函数
	Date()
	{}
	//带参构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

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

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

注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明


我们一般不这样写构造函数,一般用全缺省参数来写,这样无参带参的函数都可以调用这个函数,不用写多个构造函数。

class Date
{
public:
	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;//不传参数,使用缺省值
	Date d2(2025, 1, 1);//传了参数,使用传来的实参
	d1.Print();
	d2.Print();
}

5、如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

6、类的每个成员变量都有其对应的构造函数,一个类默认生成的构造函数其实是去调用各个成员变量对应的构造函数

通过第五个特性,我们会发现默认构造函数好像没有什么用,其实不然。

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型。

默认构造函数是不会处理内置类型的。

默认构造函数会调用成员变量的构造函数,如果成员变量没有显式定义构造函数,就调用默认生成的构造函数,否则就调用显式定义的构造函数。

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 6;
		_minute = 6;
		_second = 6;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 内置类型
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

实例化对象d时调用了Date类的默认构造函数,而这个函数又去分别调用了int类型的默认构造函数,调用了3次(_year,_month,_day)和调用了Time类型的构造函数(Time类型有显式定义构造函数),int属于内置类型,不会处理,而Time属于自定义类型,故进行处理,调用了其显式构造函数


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

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

注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为

是默认构造函数。(都是可以不传参调用

class Date
{
public:
    //无参构造函数
	Date()
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
    //全缺省构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

void Test()
{
	//调用时存在歧义
	Date d1;
}

3、初始化列表

我们都知道默认构造函数会去调用成员变量的构造函数,当Date构造函数显式定义时,仍会调用成员变量构造函数,这都是初始化列表干的。

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 6;
		_minute = 6;
		_second = 6;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

初始化列表用于在对象的构造函数中初始化成员变量,初始化列表使用冒号(:)和逗号(,)来分隔成员变量和初始值。

初始化列表的作用是在对象的构造函数中直接对成员变量进行赋值,从而避免了先默认构造再赋值的操作,减少了对象的构造和初始化时间。

只有显式定义的构造函数才存在初始化列表。

class Date
{
public:
    //使用初始化列表可以在括号写入表达式、变量等,不再拘束于定值
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

注意:

每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

类中包含以下成员,必须放在初始化列表位置进行初始化

引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)

那我们能不能考虑用赋值初始化来初始化类成员呢?我们先来看看两者的区别

成员变量的默认值只能指定数据。
初始化列表则可以在括号中写入表达式,变量等等。

成员变量的默认值并没有完成真正的赋值,它是处于对变量的声明阶段,只是告诉编译器我将要使用一个名字为xxx的变量,还没有正式开辟空间。
初始化列表则是对变量的定义阶段,也就是完成了变量的第一次赋值。

class Date
{
public:
    //使用初始化列表可以在括号写入表达式、变量等,不再拘束于定值
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
    //赋值初始化
	int _year = 1;
	int _month = 1;
	int _day = 11;
};

引用和const要求定义时必须初始化,那就只有初始化列表能做到了,所以我们尽可能使用初始化列表。尽管初始化列表能完成绝大部分初始化工作,但仍有无法完成全部的情况,当初始化列表搞不定时,还需要使用函数体,可以混用。

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{}

	void Print()
	{
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};


int main()
{
	A aa(3);
	aa.Print();
}

说明成员变量在类中声明次序确实是其在初始化列表中的初始化顺序。

4、析构函数

概念

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

析构函数名是在类名前加上字符 ~。它无参数也无返回值类型。一个类只能有一个析构函数

未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载

对象生命周期结束时,C++编译系统系统自动调用析构函数,不需要我们手动调用。

class exa 
{
public:
	//构造函数
	exa()
	{
		a = (char*)malloc(sizeof(char) * 4);
		size = 4;
	}
	//析构函数
	~exa()
	{
		free(a);
		a = nullptr;
		size = 0;
	}

private:
	char* a;
	int size;
};

看到上述代码,创建exa对象时,调用构造函数,自动初始化了一块空间给a,当对象生命周期结束,会自动调用析构函数,释放这块空间,如果没有释放,就会造成内存泄漏,析构函数的好处是我们无需手动调用。

与构造函数不同的是:一个函数只允许存在一个析构函数,不允许重载

当我们不写析构函数,编译器会默认生成一个析构函数。(这是六大默认成员函数的共有的特性)

对于默认析构函数,如果成员变量中存在其他类的对象,则会调用其他类的析构函数

注意:当我们建立了动态内存时,不能依靠默认的析构函数释放,必须自己手动释放,因为默认的析构函数仅释放类的对象调用的资源,对象是创建在栈帧的,程序结束即会销毁,而没有销毁动态内存。

5、拷贝构造函数

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

拷贝构造是必须只能有一个参数,且参数类型必须是classname&,如下述代码,其拷贝构造函数只有一个参数类型为A&。

class A
{
public:
	A(int a=0,int b=0)
		:_a(a)
		,_b(b)
	{}
	//拷贝构造函数,和构造函数构成重载
	A(const A& a)
	{
		_a = a._a;
		_b = a._b;
	}
private:
	int _a;
	int _b;
};

int main()
{
	A a1(10);
	//调用拷贝构造
	A a2(a1);
	return 0;
}

如何调用拷贝构造呢?

被动调用:对象在传参和做返回值的时候需要创建一个临时变量,这个过程调用了拷贝构造。

是底层在偷偷调用

主动调用:

//由于是构造函数的重构,所以也可以使用构造函数的语法

A a1;

A a2(a1);

//能在创建对象时用赋值语句

A a1;

A a2 = a1;

拷贝构造函数特性

1、拷贝构造函数是构造函数的一个重载形式(写了拷贝构造就不会生成默认构造)。
2*、拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
3*、若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝(即一个字节一个字节地把成员变量拷贝过去)。
4、类的每个成员变量都有对应的拷贝构造函数,一个类默认生成的拷贝构造函数其实是去调用成员变量对应类型的拷贝构造函数。(内置类型对应的拷贝构造函数会进行处理,这一部分的特性和前面的构造函数有一些不同)
5*、类中如果没有涉及资源申请时,拷贝构造函数是否写都可以。一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
第2、3和5的特性解析

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

class A
{
public:
	A(int a = 0, int b = 0)
		:_a(a)
		, _b(b)
	{}
	//如果此处参数类型为A,会发生无限递归
	//A(A a)
//	A(A a)//错误的写法
//	{
//		_a = a._a;
//		_b = a._b;
//	}

	//正确的写法
	A(const A& a)
	{
		_a = a._a;
		_b = a._b;
	}
private:
	int _a;
	int _b;
};

int main()
{
	A a1(10);
	//调用拷贝构造
	A a2(a1);
	return 0;
}

由于每次把对象作为函数形参传递时,都会调用其拷贝函数来进行拷贝,所以就导致下图的情况。

以此类推,将会造成无限递归,程序必定崩溃。

小结:对象在传参和做返回值的时候需要创建一个临时变量,这个过程调用了拷贝构造。我们一定要特别注意写法规范。

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

如上述代码,并未显式定义A的拷贝构造函数,此时会调用默认的拷贝构造函数

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

默认的拷贝构造对于内置类型,会直接拷贝,对于自定义类型,则会调用相应的拷贝构造。

所以当涉及到资源申请时,不能单单用值拷贝,值拷贝并没有实现拷贝出来的对象的空间独立,会导致拷贝出来的对象于被拷贝的对象指向同一块内存空间,程序结束会造成多次释放一块空间,导致程序崩溃,必须要自己写拷贝构造函数。

6、运算符重载

根据C语言语法,基本类型可以被各种运算符处理,比如int与int类型经由+运算符处理可以得到两者相加,那么在面向对象的C++中,我们想要实现自定义类型的运算,比如Date类中,我们希望能够实现和基本类型一致的运算,Date+int是一个日期加几天,要怎么实现呢?

这时就需要用到运算符重载了。

函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

返回值类型 operator 运算符(参数列表)
{
    // 实现运算符功能的代码
}

我们来看看下面这个例子

class A
{
public:
	A(int a = 0, int b = 0)
		:_a(a)
		, _b(b)
	{}
	bool operator==(const A& x)
	{
		return (x._a == _a) && (x._b == _b);
	}
private:
	int _a;
	int _b;
};

int main()
{
	A a1(5,10);
	A a2(5,10);
	A a3(10, 20);
	//(a1 == a2)实际上就是a1.operator==(&a1,a2)
	//cout本质也是函数重载,不加括号就会先和a1结合
	cout << (a1 == a2) << endl;//输出1
	cout << (a2 == a3) << endl;//输出0

}

注意事项:

(3)注意
1、不能通过连接其他符号来创建新的操作符:比如operator@重载操作符必须有一个类类型参数。
2、作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
3、*(解引用)、::(作用域限定符)、 sizeof 、?:(三目操作符)、 .(类成员访问操作符)、 注意以上5个运算符不能重载。
4、运算符重载的结合顺序与运算符优先级、结合性相关。
5、运算符重载大多数情况下作为类的成员函数,做不了的情况下只能做成正常函数,本质都是替换成函数调用。

6、用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。

7、赋值运算符重载

赋值运算符重载格式

1、参数类型:const T&,传递引用可以提高传参效率。

2、返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值。

3、检测是否自己给自己赋值。

4、返回*this :要复合连续赋值的含义。

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

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

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2;
	Date d3(2025,2,28);
	d1 = d2 = d3;
	//连续赋值,相当于先d2 = d3(d2.operator=(&d2,d3)),返回值为d2的引用
	//然后d1 = d2(相当于d1.operator=(&d1,d2))
	return 0;
}

由于赋值重载也是六大默认成员函数,所以没有显式定义实现赋值重载时也会生成一个默认的赋值重载,这个赋值重载是浅拷贝。它的内部规则和构造函数几乎相同,即内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

对比拷贝构造方式

Date d1(2025,2,28);
Date d2(d1);
Date d3 = d1;

Date d3 = d1,是定义,调用的是拷贝构造

d1 = d3时,是赋值,调用的是赋值重构

=这个运算符,定义是用拷贝构造,赋值时用拷贝重构。

注意:赋值运算符只能重载成类的成员函数不能重载成全局函数。

赋值运算符如果不显式定义,编译器会生成一个默认的赋值重载。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

下面这段代码会编译失败

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

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
//private: //由于要重载成全局函数,先将权限开放
	int _year;
	int _month;
	int _day;
};

Date& operator=(Date& d1, const Date& d2)
{
	if (&d1 != &d2)
	{
		d1._year = d2._year;
		d1._month = d2._month;
		d1._day = d2._day;
	return d1;
}

8、前置++和后置++重载

class Date
{
public:
	Date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//前置++
	//返回+1后的结果就行
	Date& operator++()
	{
		_day += 1;
		return *this;
	}
	//后置++
	// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
	// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
	// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后再给this + 1
	// 而tmp是临时对象,因此只能以值的方式返回,不能返回引用
	Date operator++(int)
	{
		Date tmp(*this);
		_day += 1;
		return tmp;
	}

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

int main()
{
	Date d;
	Date d1(2023, 10, 24);
	d = d1++;//此时d:2023,10,24  d1:2023,10,25
	d = ++d1;//此时d:2023,10,26  d1:2023,10,26
	return 0;
}

Date& operator++()就是前置++的重载,而Date& operator++(int)就是后置++的重载。

前置--和后置--类似。

9、const修饰的成员

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

在函数后加上const即是告诉编译器把被隐藏的this的类型前面加上const。

无论是const对象和非const对象都可以调用const成员函数,而const对象不能调用非const成员函数。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
	}
	/*void Print() const
	{
		cout << "Print()const" << endl;
	}*/
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2025,2,28);
	d1.Print();
	const Date d2(2024, 2, 28);
	d2.Print();
	return 0;
}

原因:

不是const成员函数的话,this指针转递的类型只是Date*类型,不是const Date*类型,所以此时const对象不能调用这个函数,这属于权限放大,是不允许的。而权限缩小可以被允许,所以非const对象也能调用const成员函数。

成员函数定义规则:

1、能定义成const的成员函数都应该定义成const,这样const对象和非const对象都可以调用。
2、要修改成员变量的成员函数,不能定义成const,const对象不能调用(很合理),非const才能调用。

10、取地址及const取地址重载

取地址重载和const取地址重载,就是对&操作符的重载。一般不用重新定义 ,编译器默认会生成。只有特殊情况,才需要重载,比如想让别人获取到指定的内容。

//取地址
Date* operator&()
{
	return this;
}
//const取地址
const Date* operator&() const
{
	return this;
}

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

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

相关文章

MySQL 事务笔记

MySQL 事务笔记 目录 事务简介事务操作事务四大特性并发事务问题事务隔离级别总结 事务简介 事务&#xff08;Transaction&#xff09;是数据库操作的逻辑单元&#xff0c;由一组不可分割的SQL操作组成。主要用于保证&#xff1a; 多个操作的原子性&#xff08;要么全部成功…

网络空间安全(7)攻防环境搭建

一、搭建前的准备 硬件资源&#xff1a;至少需要两台计算机&#xff0c;一台作为攻击机&#xff0c;用于执行攻击操作&#xff1b;另一台作为靶机&#xff0c;作为被攻击的目标。 软件资源&#xff1a; 操作系统&#xff1a;如Windows、Linux等&#xff0c;用于安装在攻击机和…

HarmonyOS学习第11天:布局秘籍RelativeLayout进阶之路

布局基础&#xff1a;RelativeLayout 初印象 在 HarmonyOS 的界面开发中&#xff0c;布局是构建用户界面的关键环节&#xff0c;它决定了各个组件在屏幕上的位置和排列方式。而 RelativeLayout&#xff08;相对布局&#xff09;则是其中一种功能强大且灵活的布局方式&#xff0…

【2025年2月28日稳定版】小米路由器4C刷机Immortalwrt 23.05.4系统搭载mentohust 0.3.1插件全记录

小米路由器4C刷机Immortalwrt系统搭载mentohust插件全记录 首先将路由器按住后面的reset&#xff0c;用一个针插进去然后等待5s左右&#xff0c;松开&#xff0c;即可重置路由器。 然后要用物理网线物理连接路由器Lan口和电脑&#xff0c;并将路由器WAN口连接至网口。确保电脑…

【SpringBoot+Vue】博客项目开发二:用户登录注册模块

后端用户模块开发 制定参数交互约束 当前&#xff0c;我们使用MybatisX工具快速生成的代码中&#xff0c;包含了一个实体类&#xff0c;这个类中包含我们数据表中的所有字段。 但因为有些字段&#xff0c;是不应该返回到前端的&#xff0c;比如用户密码&#xff0c;或者前端传…

idea + Docker + 阿里镜像服务打包部署

一、下载docker desktop软件 官网下载docker desktop&#xff0c;需要结合wsl使用 启动成功的画面(如果不是这个画面例如一直处理start或者是stop需要重新启动&#xff0c;不行就重启电脑) 打包成功的镜像在这里&#xff0c;如果频繁打包会导致磁盘空间被占满&#xff0c;需…

ubuntu 20.04 安装labelmg

1. 下载安装包 下载链接&#xff1a;下载链接 2. 安装启动 # 创建labelImg的环境 conda create -n labelImg# 激活labelImg环境 source activate labelImg安装依赖 pip install pyqt5-dev-tools -i https://pypi.tuna.tsinghua.edu.cn/simple/cd requirements/pip install -…

Redis版本的EOL策略与升级路径(刷到别划走)

各位看官&#xff0c;刷到就点进来&#xff0c;大数据已经抓到你喽&#xff5e;&#x1f60a; 前言 在软件行业做服务端开发的我们&#xff0c;多多少少都会接触到Redis&#xff0c;用它来缓存数据、实现分布式锁等&#xff0c;相关八股文烂熟于心&#xff0c;但是往往会忽略具…

ExpMoveFreeHandles函数分析和备用空闲表的关系

第一部分&#xff1a;ExpMoveFreeHandles和备用空闲表的关系 ULONG ExpMoveFreeHandles ( IN PHANDLE_TABLE HandleTable ) { ULONG OldValue, NewValue; ULONG Index, OldIndex, NewIndex, FreeSize; PHANDLE_TABLE_ENTRY Entry, FirstEntry; EXHAND…

java项目之基于ssm的学籍管理系统(源码+文档)

项目简介 基于ssm的学籍管理系统实现了以下功能&#xff1a; 学生信息管理&#xff1a; 学生信息新增 学生信息修改 学籍异动管理&#xff1a; 学籍异动添加 学籍异动删除 学籍异动修改 学生学业管理&#xff1a; 学生学业添加 学生学业修改 学生学业删除 学院信息管理&am…

SpringBoot+Redis+Mybatis-plus黑马点评

短信登录 基于Session实现登录 流程&#xff1a; 发送短信验证码-->短信验证码注册登录-->校验登录状态&#xff08;保存用户到ThreadLocal&#xff0c;方便后续使用&#xff09; 不能每次请求服务都要进行登录状态校验&#xff0c;解决办法&#xff1a;拦截器 在Sp…

[STM32]从零开始的STM32 BSRR、BRR、ODR寄存器讲解

一、前言 学习STM32一阵子以后&#xff0c;相信大家对STM32 GPIO的控制也有一定的了解了。之前在STM32 LED的教程中也教了大家如何使用寄存器以及库函数控制STM32的引脚从而点亮一个LED&#xff0c;之前的寄存器只是作为一个引入&#xff0c;并没有深层次的讲解&#xff0c;在教…

DeepSeek-V3关键技术之一:DeepSeekMoE

DeepSeekMoE 是一种创新的大规模语言模型架构&#xff0c;旨在通过高效的计算流程和优化设计&#xff0c;在保持高性能的同时显著降低计算成本。 1. 架构设计 DeepSeekMoE 基于 Transformer 架构&#xff0c;融合了以下核心技术&#xff1a; 专家混合系统&#xff08;Mixture…

Android Activity启动流程详解

目录 Activity 启动流程详细解析 1. 应用层发起启动请求 1.1 调用 startActivity() 1.2 通过 Instrumentation 转发请求 2. 系统服务处理&#xff08;AMS 阶段&#xff09; 2.1 Binder IPC 通信 2.2 AMS 处理流程 2.3 跨进程回调 ApplicationThread 3. 目标进程初始化…

夜天之书 #106 Apache 软件基金会如何投票选举?

近期若干开源组织进行换届选举。在此期间&#xff0c;拥有投票权的成员往往会热烈讨论&#xff0c;提名新成员候选人和治理团队的候选人。虽然讨论是容易进行的&#xff0c;但是实际的投票流程和运作方式&#xff0c;在一个成员众多的组织中&#xff0c;可能会有不少成员并不清…

保姆级教程:用Chart.js实现柱状图与折线图联动

保姆级教程:用Chart.js实现柱状图与折线图联动 ▲ 最终实现的交互式组合图表效果 一、技术原理剖析 1.1 Chart.js渲染机制 Chart.js基于HTML5 Canvas实现图表绘制,其核心原理包括: 数据绑定:将数据对象映射为图形元素分层渲染:通过order属性控制图层叠加顺序坐标系计算:…

初阶MySQL(两万字全面解析)

文章目录 1.初识MySQL1.1数据库1.2查看数据库1.3创建数据库1.4字符集编码和排序规则1.5修改数据库1.6删除数据库 2.MySQL常用数据类型和表的操作2.(一)常用数据类型1.数值类2.字符串类型3.二进制类型4.日期类型 2.(二)表的操作1查看指定库中所有表2.创建表 3.查看表结构和查看表…

4.3MISC流量分析练习-wireshark-https

流量分析题目的例题 1.了解wireshark的过滤方式 2.了解tls跟ssl协议基本还原 3.了解xor基本变换方式&#xff0c;获取flag 附件是一个流量包&#xff0c;打开之后有各种流量&#xff0c;但是分析无果&#xff0c;然后丢到kali中使用binwalk进行分析&#xff0c;发现有一个r…

STM32CubeMx DRV8833驱动

一、DRV8833驱动原理 ​ STBY口接单片机的IO口&#xff0c;STBY置0电机全部停止&#xff0c;置1才能工作。STBY置1后通过AIN1、AIN2、BIN1、BIN2 来控制正反转。 AIN1AIN2电机状态00停止1speed反转speed1正转11停止 其中A端&#xff08;AIN1与AIN2&#xff09;只能控制AO1与…

【Qt】ffmpeg照片提取、视频播放▲

目录 一、图像的成像原理&#xff1a; RGB成像原理&#xff1a; YUV成像原理&#xff1a; 二、多线程 三、ffmpeg解码&#xff08;照片提取&#xff09; 1.准备工作 &#xff08;1&#xff09;在工程文件夹里面新建三个文件夹 &#xff08;2&#xff09;在main函数中加…