lesson-2C++类与对象(中)

news2024/11/17 4:46:30

个人主页:Lei宝啊 

愿所有美好如期而遇


目录

类的6个默认成员函数

构造函数

概念

特性

析构函数

概念

特性

拷贝构造函数

概念

特性

赋值运算符重载

运算符重载

赋值运算符重载

前置++和后置++重载

日期类的实现


类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
class null   //null是类名
{

};
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员
函数。

构造函数

概念

我们可以先通过一个对象的初始化函数引入。

#include <iostream>
using namespace std;

class Date
{

public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

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

int main()
{
	Date a;

	a.Init(2023, 10, 20);
	a.Print();

	return 0;
}

每一次我们创建出一个对象都要手动为其初始化,如果忘了的话,轻点是随机值,重点程序就崩了,所以我们的构造函数就解决了这个问题。

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

看代码。 

#include <iostream>
using namespace std;

class Date
{

public:
	
    //构造函数支持重载
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}


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

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

int main()
{
	Date a;
	a.Print();
	
	return 0;
}

 我们可以看到我们a对象里的年月日确实有值了,被我们所写的的默认构造函数所调用,并全部赋值为1.

接下来我们试试有参数的构造函数。

int main()
{

	Date a(2023,10,21);
	a.Print();
	
	return 0;
}

我们发现编译器调用的是我们有参数的构造函数。

特性

构造函数 是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任
并不是开空间创建对象,而是初始化对象
特征 如下:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器 自动调用 对应的构造函数。
4. 构造函数可以重载。

 在上述代码中,也体现出了构造函数的这四个特性。

但是有几点我们要注意

1:没有参数的构造函数,系统默认的构造函数,全缺省的构造函数,都叫做默认构造函数,而这三个构造函数不可同时写出,任意二者不可同时存在,我们看代码解释。

class Date
{
public:

	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	Date(int year = 10, int month = 10, int day = 10)
	{
		_year = year;
		_month = month;
		_day = day;
	}


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

int main()
{

	Date a;

	return 0;
}

因为编译器无法区分到要调用哪个构造函数,所以就报错了。

2:当我们定义了构造函数后,编译器不会再生成默认构造函数,但这样也会出现一个小问题需要我们去掌控。

class Date
{
public:

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

int main()
{

	Date a;

	return 0;
}

因为我们自定义了一个构造函数,所以系统不会再生成默认构造函数,而我们实例化的对象要调用无参的构造函数,但是我们又没有无参的构造函数,所以编译器就只能报错,我们后面会讲到有解决方法。

3.如果说我们没有写构造函数,内置类型的成员变量使用系统的默认构造函数,不会做处理,自定义类型的成员变量会去调用他自己的默认构造函数。

解释:内置类型就是系统自带的,比如int,double之类的,像类和结构体等就是自定义类型。

class Stack
{

public:
	Stack(int capacity = 4)
	{
		_capacity = capacity;
		int top = 0;
		int* a = (int*)malloc(sizeof(int) * capacity);
	}

	~Stack()
	{
		free(_a);
		_top = 0;
		_capacity = 0;
	}

private :
	int* _a;
	int _top;
	int _capacity;

};

class Queue
{
	Stack _a;
	Stack _b;
	int size;
};

int main()
{

	Stack a;
	Queue b;

	return 0;
}

细心的朋友们可能会发现size被初始化为0了,你不是说内置类型的变量不初始化吗?是的,但是不同的编译器处理结果不同,我这个是Visual Studio 2022 ,在2013下是不会给初始化的,我们想要我们写出的代码具有跨平台性,就不要寄希望于编译器会给优化,所以我们就当做他不会给优化处理,写出优质代码。

4:C++11新特性,允许声明成员变量时给默认值。

class Date
{

public:
	void Print()
	{
		cout << _year << _month << _day;
	}

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

int main()
{

	Date a;
	a.Print();

	return 0;
}

析构函数

概念

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

特性

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

 我们来测试一下。

class Stack
{

public:
	Stack(int capacity = 4)
	{
		_capacity = capacity;
		int top = 0;
		int* a = (int*)malloc(sizeof(int) * capacity);
	}

	~Stack()
	{
		free(_a);
		_top = 0;
		_capacity = 0;
		cout << "haha" << endl;
	}

private :
	int* _a;
	int _top;
	int _capacity;

};

class Queue
{
	Stack _a;
	Stack _b;
	int size;
};

int main()
{

	Queue b;

	return 0;
}

结果显而易见,我们的确是调用了析构函数。

拷贝构造函数

概念

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

特性

我们先来写一段代码证明拷贝构造函数的必要性

首先是日期类的测试

class Date
{

public:
	Date(int year = 2023, int month = 10, int day = 22)
	{
		_year = year;
		_month = month;
		_day = day;

		cout << "Date的构造" << endl;
	}
	
private:
	int _year;
	int _month;
	int _day;

};

void func1(Date a)
{
	cout << "func1(Date a)" << endl;
}

int main()
{

	Date d1(2023, 10, 22);
	func1(d1);	

	return 0;

}
注意日期类函数调用的析构函数是系统默认的,而且也没什么需要释放的。
所以在我们将d1这个对象传给func1这个函数的时候时值传递,当fun1结束时,a这个对象要销毁,会去调用析构函数。
接下来我们来看栈这个类
class Stack
{

public:
	Stack(int capacity)
	{
		_capacity = capacity;

		int top = 0;
		_a = (int*)malloc(sizeof(int) * _capacity);
		if (_a == nullptr)
		{
			perror("malloc");
		}

		cout << "Stack的构造" << endl;
	}

	~Stack()
	{
		free(_a);
		_a = nullptr;

		_top = 0;
		_capacity = 0;

		cout << "Stack的析构" << endl;
	}
	
private:
	int* _a;
	int _top;
	int _capacity;

};

void func2(Stack st)
{
	//...
}

int main()
{
	
	Stack st1(4);
	func2(st1);

	return 0;

}

这个栈类再这么调用就会出问题,当st销毁时,去调用我们所写的析构函数,在func2函数结束时会释放一次_a,当主函数中的对象st1销毁时,会对已经释放的那块空间再释放一次,这样程序就崩溃了,因为此时的_a就是野指针了,别忘了我们是传值,空间释放后_a就成了野指针,释放野指针指向的空间是不合法的,所以就崩了。

那也许有人会说,我传引用不就好了吗,但是假设我们有一个需求,不改变对象本身,就是要拷贝一份去实现,那么拷贝构造就凸显出作用来了。(如果还有人说栈这个类我用系统默认的析构函数不好吗?那您可真是昏了头了)

拷贝构造函数也是特殊的成员函数,其 特征 如下:
1. 拷贝构造函数 是构造函数的一个重载形式
Stack(int capacity)
{
	_capacity = capacity;

	int top = 0;
	_a = (int*)malloc(sizeof(int) * _capacity);
	if (_a == nullptr)
	{
		perror("malloc");
	}

	cout << "Stack的构造" << endl;
}

//拷贝构造
Stack(const Stack& st)
{
	_top = st._top;
	_capacity = st._capacity;
	_a = (int*)malloc(sizeof(int) * _capacity);
	if (_a == nullptr)
	{
		perror("malloc");
	}
}

2. 拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错
因为会引发无穷递归调用。
拷贝构造参数不引用为什么会引发无穷递归呢?
首先要明白在传值拷贝自定义类型定义出的对象时,编译器会去调用他的拷贝构造,我们传值传参Stack类型的对象,也就是要拷贝一份给参数,所以编译器会去调这个对象的拷贝构造,我们去掉引用后的拷贝构造是这个样子的
Stack(const Stack st)
{
	_top = st._top;
	_capacity = st._capacity;
	_a = (int*)malloc(sizeof(int) * _capacity);
	if (_a == nullptr)
	{
		perror("malloc");
	}
}

接下来我们通过图解来看一看无穷递归的出现原因。

所以我们要加上&。

3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
我们的日期类是不用我们再去单独写拷贝构造函数的,因为系统默认的拷贝构造函数就是值拷贝。
拷贝构造函数典型调用场景:
使用已存在对象创建新对象
Stack st2(st1);
函数参数类型为类类型对象
当然,一般来说,没有特别要求,我们还是推荐使用引用的,效率会高些。
void func2(Stack st)
{
	//...
}
函数返回值类型为类类型对象
Date func3(Date d)
{
	//如果出了作用域,传值给d的对象还存在,
	//那么引用返回更好,这里我们只是为了举例子
	//而且我们是推荐传引用的
	return d;
}

返回的时候,d已然销毁,所以我们返回的其实是d的拷贝。

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用
尽量使用引用。

赋值运算符重载

这里我们还是会通过一些例子来引入这个概念。

我们打算写个日期类来解释,在此之前,我们需要先明白,日期和日期的相加减有没有意义,比如

2023-10-23 + 2023-10-24,显然是没有意义的,但是他们之间相减就有了意义;两个日期相差多少天,那么日期间的乘除也是没有意义的;再看2023-10-23 + 50天,这个显然也是有意义的,计算50天日期是多少;这么说的话,其实日期的比较也是有意义的,相等和大小。

那么我们如何去实现日期间的比较和日期加减天数呢?

但是这些成员变量都是私有的,在类外我们不能直接用,当然,我们可以在类里写上GetYear函数等来取得他们的值进行比较,我们这里只是测试,就先将他们公开,当然这样的代码是不健康的,但是我们也说了,只是测试。

class Date
{

public:
	Date(int year = 2023, int month = 10, int day = 22)
	{
		_year = year;
		_month = month;
		_day = day;

		cout << "Date的构造" << endl;
	}

	int _year;
	int _month;
	int _day;

};

bool equal(Date d1, Date d2)
{
	if (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day)
		return true;
	else
		return false;
}

int main()
{

	Date d1;
	Date d2(2023, 2, 13);

	bool ret = equal(d1, d2);
	cout << ret << endl;

	return 0;
}

当然,这样会更好些

bool equal(const Date& d1, const Date& d2)
{
	if (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day)
		return true;
	else
		return false;
}

 接下来进行大小比较函数,起个什么名字好呢?compare?比大还是比小?我怎么知道,compare_large? 当下,我们先随便起名字。

class Date
{

public:
	Date(int year = 2023, int month = 10, int day = 22)
	{
		_year = year;
		_month = month;
		_day = day;

		cout << "Date的构造" << endl;
	}

	int _year;
	int _month;
	int _day;

};

bool equal(const Date& d1, const Date& d2)
{
	if (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day)
		return true;
	else
		return false;
}

bool compare(const Date& d1, const Date& d2)
{

	if (d1._year > d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day)
	{
		return true;
	}

	return false;
}

int main()
{

	Date d1;
	Date d2(2023, 2, 13);

	bool ret1 = equal(d1, d2);
	bool ret2 = compare(d1, d2);

	cout << ret1 << endl;
	cout << ret2 << endl;

	return 0;
}

 

我们可以想想内置类型定义出的变量,都是可以直接比较和相加减的,那么我自定义类型定义的对象能不能也直接比较相加减呢?答案是不能,编译器无法理解对象如何比较,比如我们的d1和d2,怎么确定大小呢?年月日都大于才算大于吗?由此,我们引入一个新的概念,运算符重载,不进解决了函数名不便于理解的问题,还可以进行对象的比较。

运算符重载

C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字 operator 后面接需要重载的运算符符号
函数原型: 返回值类型  operator 操作符 ( 参数列表 )
看代码前别忘了六个成员函数每个函数都有一个隐含的this指针。
class Date
{

public:
	Date(int year = 2023, int month = 10, int day = 22)
	{
		_year = year;
		_month = month;
		_day = day;

		cout << "Date的构造" << endl;
	}
    
    //比较相等
	bool operator==(const Date& d2)
	{
		if (_year == d2._year &&_month == d2._month && _day == d2._day)
			return true;
		else
			return false;
	}

    //比较大于
	bool operator>(const Date& d2)
	{

		if (_year > d2._year)
		{
			return true;
		}
		else if (_year == d2._year && _month > _month)
		{
			return true;
		}
		else if (_year == d2._year && _month == d2._month && _day > d2._day)
		{
			return true;
		}

		return false;
	}

private:
	int _year;
	int _month;
	int _day;

};

int main()
{

	Date d1;
	Date d2(2023, 2, 13);

	//bool ret1 = equal(d1, d2);
	//bool ret2 = compare(d1, d2);

	bool ret1 = d1 == d2; //d1.operator==(d2);
	bool ret2 = d1 > d2;  //d1.operator>(d2);

	cout << ret1 << endl;
	cout << ret2 << endl;

	bool ret3 = d1.operator==(d2);
	bool ret4 = d1.operator>(d2);

	cout << ret1 << endl;
	cout << ret2 << endl;

	return 0;
}
注意:
  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

写在类外面,只写一个参数就是如下图结果; 

 

因为大于的比较有两个操作数。

接下来是日期加上一个数。

·

class Date
{

public:
	Date(int year = 2023, int month = 10, int day = 22)
	{
		_year = year;
		_month = month;
		_day = day;

		cout << "Date的构造" << endl;
	}

	int GetMonthday(int year, int month)
	{
		int month_day[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;
		}

		return month_day[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;
	}

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

private:
	int _year;
	int _month;
	int _day;

};

int main()
{

	Date d1;
	d1 += 50;

	d1.Print();

	return 0;
}

但是这样我们把对象d1就做了修改,我们不想修改,只想+然后返回来一个值怎么办?

将下面这个代码加入类中

	Date operator+(int day)
	{
        //实例化一个对象tmp拷贝*this
		Date tmp(*this);

        //复用operator+=
		tmp += 50;
	
		return tmp;
	}

int main()
{

	Date d1;

	Date d2 = d1 + 50;
	d2.Print();

	return 0;
}

 

接下来就是我们的赋值运算符重载了。

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

	return *this;
}

前置++和后置++重载

前置和后置++,最难以区分的其实是他们的名字,尽管返回值不同,但参数相同,不构成重载,于是就有了一个特殊规定,看代码

C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递
//前置++复用operator+=
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

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

	return tmp;
}

于是我们用参数类型来区分,一个不传,一个传int类型,编译器就可以区分了。

一般情况下我们最好还是用前置++,是引用返回,而且没有拷贝,后置++就有一次拷贝,也许一次调用差距不大,但是如果调用10万次就有差别了。

日期类的实现

接下来我们将使用各种运算符重载实现各种有意义的日期比较和加减,

头文件 

#pragma once
#include <iostream>
using namespace std;

class Date
{

public:

	Date(int year = 2023, int month = 10, int day = 24);
	
	int GetMonthday(int year, int month);
	
	//operator+复用opreator+=,优于operator+=复用operator+
	Date operator+(int day);
	Date operator-(int day);        //计算日期减天数
	int operator-(const Date& d);   //计算日期和日期间差几天
	Date& operator+=(int day);
	Date& operator-=(int day);
	Date& 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);
	bool operator<=(const Date& d);
	
	//前置++复用operator+=
	Date& operator++();
	//后置++复用operator+=
	Date operator++(int);
	
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;

};
 

日期类的实现 

#include "Date.h"

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

int Date::GetMonthday(int year, int month)
{
	int month_day[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;
	}

	return month_day[month];
}

Date& Date::operator+=(int day)
{
	_day += day;

	while (_day > GetMonthday(_year, _month))
	{
		_day -= GetMonthday(_year, _month);
		_month++;

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

	return *this;
}

Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += 50;

	return tmp;
}

Date  Date::operator-(int day)
{
	Date tmp(*this);
	tmp -= day;

	return tmp;
}

Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}

	_day -= day;
	while (_day <= 0)
	{
		--_month;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}

		_day += GetMonthday(_year, _month);

	}
}


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

	return *this;
}

//前置++复用operator+=
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

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

	return tmp;
}

bool Date::operator>(const Date& d)
{

	if (_year > d._year)
	{
		return true;
	}
	else if (_year == d._year && _month > _month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _day > d._day)
	{
		return true;
	}

	return false;
}

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 !(*this == d);
}

bool Date::operator<(const Date& d)
{
	return !(*this >= d);
}

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

bool Date::operator<=(const Date& d)
{
	return !(*this <= d);
}


int Date::operator-(const Date& d)
{
	int flag = 1;
	Date max = *this;
	Date min = d;

	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}

	return n * flag;
}

测试 

#include "Date.h"

int main()
{
	
	Date d1;

	//测试赋值运算符重载
	Date d2 = d1;
	cout << "测试d2赋值运算符重载" << endl;
	d2.Print();

	//测试+运算符重载
	Date d3(2023, 10, 25);
	d3 = d3 + 50;
	cout << "测试d3+运算符重载" << endl;
	d3.Print();

	//测试-运算符重载
	Date d4(2023, 10, 25);
	d4 = d4 - 50;
	cout << "测试d4-运算符重载" << endl;
	d4.Print();

	//测试==运算符重载
	Date d5(2023, 10, 25);
	d5 = d5 - 50;
	cout << "测试d5==运算符重载" << endl;
	cout << (d4 == d5) << endl;

	//测试!=运算符重载
	Date d6(2023, 10, 25);
	cout << "测试d6!=运算符重载" << endl;
	cout << (d4 == d6) << endl;

	//测试++运算符重载
	Date d7(2023, 10, 25);
	cout << "测试d7++运算符重载" << endl;
	++d7;
	d7.Print();

	//测试>=运算符重载
	Date d8(2023, 10, 25);
	cout << "测试d8>=运算符重载" << endl;
	cout << (d7 >= d8) << endl;
	
	cout << endl << "全部通过" << endl;
	return 0;
}

测试结果 

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

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

相关文章

基于java jsp垃圾分类管理系统的设计与实现

摘 要 我们的时代像一辆高速飞驰的列车&#xff0c;带着互联网冲入了我们的视野内&#xff0c;并且大家对生活品质的追求也更加地高了。花变成了人们生活中的一个常见品。鲜花的需求量在这些年来逐步增长&#xff0c;花本身就具有“高颜值”&#xff0c;还伴有特殊的香味&a…

内核进程的调度与进程切换

进程被创建到了链表中&#xff0c;如何再进行进一步的调用和调用&#xff1f; 进程调度 void schedule(void)&#xff1b; 进程调度 switch_to(next); 进程切换函数 void schedule(void) {int i,next,c;struct task_struct ** p;/* check alarm, wake up any i…

秒级启动的集成测试框架

本文介绍了一种秒级启动的集成测试框架&#xff0c;使用该框架可以方便的修改和完善测试用例&#xff0c;使得测试用例成为测试过程的产物。 背景 传统的单元测试&#xff0c;测试的范围往往非常有限&#xff0c;常常覆盖的是一些工具类、静态方法或者较为底层纯粹的类实现&…

群晖上搭建teamspeak3语音服务器

什么是 TeamSpeak &#xff1f; TeamSpeak &#xff08;简称 TS&#xff09;是一款团队语音通讯工具&#xff0c;但比一般的通讯工具具有更多的功能而且使用方便。它由服务器端程序和客户端程序两部分组成&#xff0c;如果不是想自己架设 TS 服务器&#xff0c;只需下载客户端程…

OpenCV学习(四)——轨迹栏(调色板与不同通道图像)

轨迹栏 4. OpenCV轨迹栏4.1 轨迹栏作为调色板4.2 轨迹栏显示不同通道图像 4. OpenCV轨迹栏 会用到以下主要两个函数 cv2.createTrackbar(trackbarName, windowName, value, count, onChange)创建轨迹栏 主要参数&#xff1a; trackbarName&#xff1a;轨迹栏名称windowName…

视频分辨率/帧率/码率选择参考

1. 视频码率与分辨率的参考表 1080&#xff0a;720的分辨率&#xff0c;用5000K左右&#xff1b; 720&#xff0a;576的分辨率&#xff0c;用3500K左右&#xff1b; 640&#xff0a;480的分辨率&#xff0c;用1500K左右。 2. 计算公式 基本算法&#xff1a;码率&#xff08;kb…

基于Canal同步MySQL数据到Elasticsearch

基于Canal同步MySQL数据到Elasticsearch 基于 canal 同步 mysql 的数据到 elasticsearch 中。 1、canal-server 相关软件的安装请参考&#xff1a;《Canal实现数据同步》 1.1 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmln…

中国地名信息库

地名是社会基本公共信息&#xff0c;是历史文化的重要载体。 2014年至2018年&#xff0c;国家启动实施并完成了第二次全国地名普查工作&#xff0c;全国共计采集地名1320多万条&#xff0c;修测标绘地名图2.4万多幅&#xff0c;新设更新地名标志68万多块&#xff0c;普遍建立了…

server2012 通过防火墙开启局域网内限定IP进行远程桌面连接

我这里需要被远程桌面的电脑系统版本为windows server2012 1、打开允许远程连接设置 2、开启防火墙 3、设置允许“远程桌面应用”通过防火墙 勾选”远程桌面“ 3、入站规则设置 高级设置→入站规则→远程桌面-用户模式(TCP-In) 进入远程桌面属性的作用域——>远程IP地址—…

演讲比赛常见误区及解决方法

演讲比赛常见误区及解决方法 一、演讲内容选择错误 1. 主题选择不合理 许多参赛者选择的主题内容&#xff0c;与比赛题目要求或听众背景不符&#xff0c;难以引起听众的兴趣。正确选择主题应考虑以下几点&#xff1a; - 主题应与比赛题目要求相符合&#xff0c;切合比赛定位…

《C和指针》(5)操作符和表达式

问题 下面这个表达式的类型和值分别是什么? 答&#xff1a;该值为2.0&#xff0c;如果要进行浮点除法&#xff0c;请使用以下表达式 下面这个程序的结果是什么&#xff1f; 答&#xff1a;这是一个狡猾的问题。比较明显的回答是-10(2-3 *4),但实际上它因编译器而异。乘法运…

Android S从桌面点击图标启动APP流程 (五)

系列文章 Android S从桌面点击图标启动APP流程 (一)Android S从桌面点击图标启动APP流程 (二) Android S从桌面点击图标启动APP流程 (三) Android S从桌面点击图标启动APP流程 (四) Android S从桌面点击图标启动APP流程 (五) Android S从桌面点击图标启动APP流程 (六) An…

17、简单记录一下两个流媒体工具和推流测试

基本思想:在开发流媒体服务过程中,使用了两个流媒体工具,这里做一下简单的记录,以后可以翻阅和查看 一:流媒体服务工具之一:https://github.com/bluenviron/mediamtx/releases 它支持rtsp/rtmp/hls推流测试 二、流媒体工具:Releases EasyDarwin/EasyDarwin GitHub 该…

华为认证H12-831考试新增题库

279、 以下哪些数列能被正则表达式[^100|200]$匹配? A、300 200 100 B、200 100 300 C、100 200 300 D、100 300 200 试题答案&#xff1a;BC 试题解析&#xff1a;[^ ]表示不包括字符&#xff0c;$表示以某字符结尾。题目的正则表达式表示不以100 200 结…

【CMake】windows10下入门课程

【CMake】windows10下入门课程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【CMake】windows10下入门课程前言cmake安装初识cmake&#xff1a;新建helloworld项目cmake的入门使用法则总结 前言 CMake(Cross-Platform Make)是一个开源的跨平…

高效文件管理:自动生成文件夹及重命名的方法分享

在高效文件管理中&#xff0c;自动生成文件夹及重命名是一项非常实用的技巧。通过掌握这种方法&#xff0c;我们能够更轻松地整理和查找文件&#xff0c;提高工作效率。本文将分享云炫文件管理器自动生成文件夹及重命名的实用方法&#xff0c;帮助您实现高效的文件管理。现在跟…

OSPF NSSA区域配置

NSSA&#xff1a;Not-So-Stubby Area&#xff08;不太末节的区域&#xff09; 示例&#xff0c;拓朴如下&#xff1a; 思路&#xff1a; R1正常配置Area 0区域&#xff0c;R2的1口配置为区域0&#xff0c;2口配置为区域1&#xff0c;配置NSSA&#xff0c;R3配置为区域1…

如何在Windows和Linux系统上监听文件夹的变动?

文章目录 如何在Windows和Linux系统上监听文件夹的变动&#xff1f;读写文件文件系统的操作缓冲和流文件改变事件 如何在Windows和Linux系统上监听文件夹的变动&#xff1f; libuv库实现了监听整个文件夹的修改。本文详细介绍libuv库文件读写和监听的的实现方法。libuv库开发了…

【计算机网络笔记】Cookie技术

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

Linux系统下安全控制策略SELinux解析

SELinux&#xff08;Security-Enhanced linux&#xff09;是增强版Linux&#xff0c;简称SELinux&#xff0c;它是一个Linux内核模块&#xff0c;也是Linux的一个安全子系统&#xff0c;主要以内核模块为支持&#xff0c;用户态程序进行权限策略管理。 背景 Linux系统下的roo…