C++拷贝构造函数和运算符重载

目录

一,拷贝构造函数

二,运算符重载


一,拷贝构造函数

概念:在类的定义中,构造函数只是单纯将内置类型进行初始化,而拷贝构造函数是将整个类进行拷贝到另一个类中进行初始化。在定义拷贝构造函数时,只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

这里要说明的是,拷贝构造函数也是特殊的成员函数,其特征如下:

        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;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1;//调用构造函数
    Date d2(d1);//用类进行初始化,会调用拷贝构造函数
    return 0;
}

引发无穷递归的过程图

        3,若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数只会进行浅拷贝,或者值拷贝。(浅拷贝是指只拷贝对象的基本数据类型和引用地址,不会拷贝引用地址所指向的对象,即原对象中的数据所指向的空间将与拷贝后的对象的数据所指向的空间相同)。

        这里要说明的是默认拷贝构造函数的处理对象不跟默认构造函数一样,但处理自定义类型的方式跟默认构造函数一样。默认拷贝构造函数会对自定义类型和内置类型都做处理,其中,内置类型成员完成浅拷贝,自定义类型成员将会调用这个成员的拷贝构造

#include <iostream>
using namespace std;
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对象的拷贝构造" << 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 d1;
    Date d2(d1);
    return 0;
}

d1类运行的调试内部分析图(构造函数)

d2类运行的调试内部分析图(拷贝构造函数)

        下面问题来了,既然有默认拷贝构造函数,我们需要不需要再定义拷贝构造函数呢?

        请注意上面说的,默认拷贝构造只会进行浅拷贝,当我们在对象成员中开辟了动态空间时,使用默认拷贝构造将会出现问题。请看以下代码:

//此代码运行时将会崩溃
#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (!_array) {
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		if (_array) {
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	Stack s2(s1);
	return 0;
}

分析: 

        首先,s1先调用构造函数创建,在构造函数中开辟了10个元素的空间,然后,s2对象使用s1拷贝构造,Stack对象中没有自己定义,系统将生成一份默认的拷贝构造函数进行浅拷贝,这时,s1和s2栈结构将同时指向一块内存空间,当函数退出时,s2和s1会自动调用析构函数进行销毁,这就会造成s1和s2指向的同一块空间销毁两次,系统崩毁。

        解决上面的问题不难,我们只需将s1和s2分别指向不同的空间即可,这时,需要我们自己定义拷贝构造函数。

#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (!_array) {
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	//定义拷贝构造
	Stack(const Stack& S)
	{
		_size = S._size;
		_capacity = S._capacity;
		_array = (DataType*)malloc(_capacity * sizeof(DataType));
		if (!_array) {
			perror("malloc申请空间失败");
			return;
		}
	}
	~Stack()
	{
		if (_array) {
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	Stack s2(s1);
	return 0;
}

总:如果类中没有涉及空间资源的申请时,拷贝构造函数可以不写,但是一旦涉及到资源空间的申请时,则拷贝构造函数是一定要写上的。

拷贝构造函数典型调用场景:

        1,使用已存在对象创建新对象。如同以上Stack类中s2的创建。

/*..........*/

int main()
{
    Stack s1;
    Stack s2(s1);//使用已存在的对象s1来创建对象s2
    return 0;
}

        2,函数参数类型为类类型对象——解析:因为在传参过程中,形参就相当于实参的临时拷贝,相当于用实参来创建形参。

        3,函数返回值类型为类类型对象——解析:当函数返回时,函数内局部对象的生命周期结束,但是临时变量的值会被拷贝到调用函数的栈帧中,或者通过引用传递给调用函数。当返回类类型对象时,直接将此类对象拷贝到调用函数栈帧中。

总结一句话,只要是运用了类对象与类对象直接赋值进行初始化的情况,系统就会调用拷贝构造。而为了提高效率,一般能用引用就用引用。


二,运算符重载

引入:

        在内置类型中,系统给我们自动定义了很大运算符,如:+、-、*、/、++、--、>等。这些内置类型和内置类型运算符的使用都是系统定义好的,可直接使用。现在问题来了,自定义类型要想使用这些运算符又当如何?由于自定义类型是我们自己定义的,系统不知道其结构,所以无法直接供我们使用,但C++为了增强代码的可读性引入了运算符重载的概念,可让自定义类型也使用这些运算符。运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

运算符重载的定义:

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

        函数原型:返回值类型 operator操作符(参数列表)。

这里有以下5个注意:

        1,不能通过连接其他符号来创建新的操作符:比如operator@

        2,重载操作符必须有一个类类型参数

        3,用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其+本身的含义

        4,作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针。

        5,“ .*    ::    sizeof    ?:    . ” 注意这5个运算符不能重载。这个经常在笔试选择题中出现。

在类的内部定义

第一个参数为this指针

// '='操作符,返回类型为类类型Date的引用
Date& operator=(const Date& d);

Date& Date::operator=(const Date& d);//此种情况是在类中声明,在外部实现,用限定符说明是类对象中的成员函数
// "+="操作符,返回类型为类类型Date的引用
Date& operator+=(int day);

Date& Date::operator+=(int day);//此种情况是在类中声明,在外部实现,用限定符说明是类对象中的成员函数
// '+'操作符,返回类型为类类型Date的引用
Date operator+(int day);

Date Date::operator+(int day);//此种情况是在类中声明,在外部实现,用限定符说明是类对象中的成员函数
// '-'操作符,返回类型为类类型Date的引用
Date operator-(int day);

Date Date::operator-(int day);//此种情况是在类中声明,在外部实现,用限定符说明是类对象中的成员函数
// "-="操作符,返回类型为类类型Date的引用
Date& operator-=(int day);

Date& Date::operator-=(int day);//此种情况是在类中声明,在外部实现,用限定符说明是类对象中的成员函数

在类的外部定义

没有默认参数,需要把类写入形参中

// "+="操作符,返回类型为类类型Date的引用
Date& operator+=(const Date& d1, int day);
// '+'操作符,返回类型为类类型Date的引用
Date operator+(const Date& d1, int day);
// '-'操作符,返回类型为类类型Date的引用
Date operator-(const Date& d1, int day);
// "-="操作符,返回类型为类类型Date的引用
Date& operator-=(const Date& d1, int day);

运用细节演示:

        (1)首先,我们先对“ >,<,>=,<=,==,!= ”这几个简单运算符在类对象内定义进行演示和解说。

#include <iostream>
#include <assert.h>
using namespace std;
//再次提醒一下,类对象中的成员函数第一个默认参数为this,且指向此对象,下面运用时就不做说明
class Date
{
public:
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// ==运算符重载
	bool operator==(const Date& d)
	{
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}
	// !=运算符重载
	bool operator != (const Date& d)
	{
		return !(*this == d);
	}
	// >运算符重载
	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;
	}
	// <运算符重载
	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;
	Date d2(2023, 10, 16);
	cout << (d1 > d2) << endl;//d1 > d2等效于d1.operator>(d2),这两个表达的意思一样
	cout << d1.operator>(d2) << endl;
	return 0;
}

        在上面代码中要说明的是在运算符表示过程中,我们即可用函数来表示运算符的含义,也可直使用运算符的形式表示含义。

在上面的运算符函数中:

d1 > d2等效于d1.operator(d2)

d1 == d2等效于d1.operator(d2)

上面的输出流中之所以(d1 > d2)用括号括起来是因为“ << ”操作符的运算级别高,会先与之运算。

        (2)接下来我们对“ +,-,+=,-= ”这几个简单运算符在类对象内定义进行演示和解说。

#include <iostream>
#include <assert.h>
using namespace std;
class Date
{
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		assert(year >= 1 && (month >= 1 && month <= 12));
		int MonthDays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		if ((month == 2) && ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)))
			return 29;
		return MonthDays[month];
	}
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 日期-天数
	Date operator-(int day)
	{
		_day -= day;
		while (_day < 0) {
			if (_month == 1) {
				_year--;
				if (_year < 0) {
					perror("Date Count Error:");
					exit(-1);
				}
				_month = 12;
			}
			else {
				_month--;
			}
			_day += GetMonthDay(this->_year, this->_month);
		}
		return *this;
	}
	// 日期-=天数
	Date& operator-=(int day)
	{
		Date DateCopy(*this);
		//用临时对象返回,相当于函数的返回,需要有个对象去接收,而改变的只是临时对象
		//像直接使用类d1 -= 5,表达的只是此函数的功能,d1的值不会改变,若是d1 = d1 -= 5将会改变
		//DateCopy = DateCopy.operator-(day);
		//return DateCopy;

		//this指针直接指向的是此对象(即此类),用this指针来改变相当于直接改变了d1,类似于达到了改变d1的效果
		*this = DateCopy.operator-(day);
		return *this;
	}
	// 日期+=天数
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(this->_year, this->_month)) {
			_day -= GetMonthDay(this->_year, this->_month);
			_month++;
			if (_month > 12) {
				_year++;
				_month -= 12;
			}
		}
		return *this;
	}
	// 日期+天数
	Date operator+(int day)
	{
		Date DateCopy(*this);
		DateCopy.operator+=(day);
		return DateCopy;
	}
	// 日期-日期 返回天数
	int operator-(const Date& d)
	{
		int YearDay = 0, MonthDay = 0, Day = _day - d._day;
		while (--_month) {
			MonthDay += GetMonthDay(_year, _month);
		}
		int month = d._month;
		while (--month) {
			MonthDay -= GetMonthDay(d._year, month);
		}
		_year--;
		int year = d._year - 1;
		for (int YearCount = _year - year; YearCount > 0; YearCount--) {
			YearDay += 365;
			if ((_year % 400 == 0) || ((_year % 4 == 0) && (_year % 100 != 0)))
				YearDay += 1;
			_year--;
		}
		return YearDay + MonthDay + Day;
	}
	void Print() {
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 10, 16);
	Date d2;
	//+=和+运算符重载
	//这里要说明的是,由于"+="没有返回值,所以在函数对象内部改变的时候一定要用this指针来改变,this指针指向d1
	d1 += 365;
	cout << "d1重载d1+=365: ";
	d1.Print();
	d2 = d1 + 23;
	cout << "d2重载d2=d1+23: ";
	d2.Print();
	//-=和-的运算符重载
	//"-="运算符与"+="同理
	d1 -= 365;
	cout << "d1重载d1-=365: ";
	d1.Print();
	d2 = d1 - 365;
	cout << "d2重载d2=d1-365: ";
	d2.Print();
	//日期-日期的运用
	cout << "d2-d1: " << d2 - d1 << endl;
	return 0;
}

        这里要注意的是函数的返回类型,“ +=,-= ”运算一般是不需要返回类型的,所以在内部实现要用this访问,从而间接实现原对象的运算。“ +,- ”操作符要有返回类型去接收,可直接返回临时对象。

        (3)“ 前置++,后置++,前置--,后置-- ”在类对象内定义进行演示和解说。

    // 后置++,形参为int,C++专门规定,为了与前置++区分
    Date operator++(int) {
        Date ret(*this);
        *this += 1;
        return ret;
    }
    // 前置++
    Date& operator++() {
        *this += 1;
        return *this;
    }
    // 后置--,形参为int,C++专门规定,为了与前置--区分
    Date operator--(int) {
      //先将原本类进行拷贝,然后将此对象里的值--,返回此对象,实现了先赋值,再--
        Date ret(*this);
        *this -= 1;
        return ret;
    }
    // 前置--
    Date& operator--() {
      //直接返回*this,实现了先把对象的值--,然后将此对象返回,实现了先--,再赋值
        *this -= 1;
        return *this;
    }

        这里要注意的是前置++、前置--和后置++、后置--的写法和前置和后置的实现。如上代码中C++的专门规定前置与后置的写法,前置的实现是先进行运算,然后再进行赋值,所以要直接用this进行。后置的实现要先进行赋值,然后再运算。这里我们虽然实现了this的运算,但返回的是运算前的对象,进而实现了此原理。

        (4)赋值运算符。赋值运算符比较特殊,我们要区分它与拷贝构造。当类与类进行赋值时,若类还没有进行初始化,将会调用拷贝构造;若已进行了初始化,将会调用赋值运算符。

        1,赋值运算符一般也要设置返回类型,一般返回 *this,因为我们要保证连续赋值的情况。

#include <iostream>
#include <assert.h>
using namespace std;
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 赋值运算符重载
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 11, 9);
	Date d2, d3, d4;
	d4 = d3 = d2 = d1;//调用赋值运算符重载
	return 0;
}

        2,赋值运算符只能重载成类的成员函数不能重载成全局函数。

class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    int _year;
    int _month;
    int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)//出现错误,出现在全局中
{
    left._year = right._year;
    left._month = right._month;
    left._day = right._day;
    return left;
}

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

        3,用户自己如果没有定义赋值运算符,编译器会生成一个默认赋值运算符重载,以浅拷贝的形式实现拷贝。内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

#include <iostream>
using namespace std;
class Time
{
public:
	Time()
	{
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time& operator=(const Time& t)
	{		
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		return *this;
	}
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 = d2;
	return 0;
}

d1 = d2运行的调试解图

        默认赋值运算符重载的原理跟默认拷贝构造一样,如果类中未涉及到资源空间的管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要自己定义实现。

        (5)流操作符的重载。在C++程序中,我们也可实现插入操作符(<<)和提取操作符(>>)的重载。

        我们首先要认识ostream和istream。ostream用于处理输出流的类,类中定义了插入操作符(<<)来将数据写入输出流;istream是用于处理输入流的类。它定义了提取操作符(>>)来从输入流中提取数据。

定义样例:

在类的内部定义

第一个参数默认this指针

class Date
{
private:
    int _year = 2023;
    int _month = 10;
    int _day = 16;
};

//没有返回类型的情况

void operator<<(ostream& out);

void operator>>(istream& in);

//在类内部声明,在外部进行定义,也相当于在内部定义

void Date::operator<<(ostream& out);

void Date::operator>>(istream& in);

//返回类型为ostream和istream,这种情况为了支持连续输入输出的情况,因为连续输入输出需要有返回值去接收

ostream& operator<<(ostream& out);

istream& operator>>(istream& in);

在类的外部定义

没有默认参数,需要把类写入形参中

class Date
{
private:
    int _year = 2023;
    int _month = 10;
    int _day = 16;
};

//没有返回类型的情况

void operator<<(ostream& out, const Date& d1);

void operator>>(istream& out, const Date& d1);

//返回类型为ostream和istream,这种情况为了支持连续输入输出的情况,因为连续输入输出需要有返回值去接收

ostream& operator<<(ostream& out, const Date& d1);

istream& operator>>(istream& out, const Date& d1);

首先我们先观察以下代码:

#include<iostream>
using namespace std;
class Date
{
public:
	void operator<<(ostream& out) 
	{
		out << _year << "/" << _month << "/" << _day << endl;
	}
	void operator>>(istream& in)
	{
		in >> _year >> _month >> _day;
	}
private:
	int _year = 2023;
	int _month = 10;
	int _day = 16;
};
int main()
{
	Date d1;
	//cin >> d1;// 相当于cin.operator>>(d1);系统报错
	d1 >> cin;// 相当于d1.operator<<(cin);正常运行
	//cout << d1;// 相当于cout.operator<<(d1);系统报错
	d1 << cout;// 相当于d1.operator<<(cout);正常运行
	return 0;
}

        因为双操作数的运算符第一个参数是左操作数,第二个参数是右操作数,而在Date对象内部中,Date类对象默认占第一个位置,导致了以上情况的发生。

        要想解决以上的问题,保证代码的可读性,就改变参数的位置,显然这不能在类对象的内部定义,必须在外部定义。

无返回类型的使用(即不支持连续使用)

#include<iostream>
using namespace std;
class Date
{
public:
	int _year = 2023;
	int _month = 10;
	int _day = 16;
};
void operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
}
void operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
}
int main()
{
	Date d1;
	cin >> d1;// 相当于cin.operator>>(d1);正常运行
	cout << d1;// 相当于cout.operator<<(d1);正常运行
	return 0;
}

运用图解

有返回类型的使用(即支持连续使用)

#include<iostream>
using namespace std;
class Date
{
public:
	int _year = 2023;
	int _month = 10;
	int _day = 16;
};
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1, d2;
	cin >> d1 >> d2;// 从左到右连续进行,返回类型为输入流类的引用
	cout << d1 << endl << d2;// 从左到右连续进行,返回类型为输出流类的引用
	return 0;
}

这里要注意几个问题:

        1,当在外部定义时,因为是在外面访问成员对象的,因此权限必须设为public。

        2,在使用 “ >> ” 运算符时,因为是要对成员对象进行输入流给值的操作,所以类对象不能加用const。

        3,连续使用运算符操作相当于连续不断的赋值操作,这里需注意函数的返回类型。

总结一下:除流操作符以外的操作符一般是实现成员函数,“ >>,<< ”流运算要在外部实现,只有这样才能让流对象做第一个参数,实现可读性。还有就是要注意函数的返回类型,因为有了返回类型,才能实现连续使用运算符的操作。

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

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

相关文章

bclinux aarch64 ceph 14.2.10 文件存储 Ceph File System, 需要部署mds: ceph-deploy mds

创建池 [rootceph-0 ~]# ceph osd pool create cephfs_data 64 pool cephfs_data created [rootceph-0 ~]# ceph osd pool create cephfs_metadata 32 pool cephfs_metadata created cephfs_metadata 64 报错 官方说明&#xff1a; 元数据池通常最多可容纳几 GB 的数据。为…

Web后端开发_01

Web后端开发 请求响应 SpringBoot提供了一个非常核心的Servlet 》DispatcherServlet&#xff0c;DispatcherServlet实现了servlet中规范的接口 请求响应&#xff1a; 请求&#xff08;HttpServletRequest&#xff09;&#xff1a;获取请求数据响应&#xff08;HttpServletRe…

正点原子嵌入式linux驱动开发——Linux DAC驱动

上一篇笔记中学习了ADC驱动&#xff0c;STM32MP157 也有DAC外设&#xff0c;DAC也使用的IIO驱动框架。本章就来学习一下如下在Linux下使用STM32MP157上的DAC。 DAC简介 ADC是模数转换器&#xff0c;负责将外界的模拟信号转换为数字信号。DAC刚好相反&#xff0c;是数模转换器…

MS512非接触式读卡器 IC

MS512 是一款应用于 13.56MHz 非接触式通信中的高集 成度读写卡芯片。它利用了先进的调制和解调技术&#xff0c;完全集 成了在 13.56MHz 下的各种非接触式通信方式和协议。 主要特点  高度集成的解调和解码模拟电路  采用少量外部器件&#xff0c;即可将输出驱动级接…

# Spring事务与分布式事务

一、事务的具体定义 事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元&#xff0c;组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交&#xff0c;只要其中任一操作执行失败&#xff08;出现异常&#xff09;&#xff0c;都将导致整个事务…

联想笔记本Fn + A可以全选,Ctrl失效

问题&#xff1a;联想笔记本Fn A可以全选&#xff0c;ctrl失效。 原因&#xff1a;BIOS启用了Fn键和Ctrl键互换。 解决操作&#xff1a; 1.开机时一直按F2&#xff0c;进入BIOS 2.点击More Settings > 2.选取Configuration 3.将Fool Proof Fn Ctrl 设定变更为Disabled 4.按…

【Linux】进程概念IV 进程地址空间

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 0. 数据在内存中的分布1. 虚拟地址与真实物理地址2. 进程地址空间2.1 进程地址空间概念2.2 进程->页表->内存 0. 数据在内…

MASK、MPSK、MFSK信号的调制与解调+星座图

MASK、MPSK、MFSK信号的调制与解调星座图 本文主要涉及多进制幅度键控&#xff08;MASK&#xff09;、多进制相移键控&#xff08;MPSK&#xff09;、多进频移键控&#xff08;MFSK&#xff09;的调制与解调&#xff0c;同时涉及到星座图的分析。 关于通信原理还有其他文章可参…

【SpringBoot整合JSP】

【源码】SpringBoot整合JSP 一、前言二、创建web项目,webapp 【创建视图层】&#xff08;一&#xff09;在 main 目录下相关目录1. 点击 “FIle”-> “Project Structure”&#xff0c;选择 “Model”-> “Web”&#xff0c;将“Web Resource Directory”的路径修改为 刚…

JOSEF约瑟 反时限过流继电器JGL-115板前接线5A速断保护

系列型号 JGL-111反时限过流继电器&#xff1b;JGL-112反时限过流继电器&#xff1b; JGL-113反时限过流继电器&#xff1b;JGL-114反时限过流继电器&#xff1b; JGL-115反时限过流继电器&#xff1b;JGL-116反时限过流继电器&#xff1b; JGL-117反时限过流继电器&#xff1b…

Python数据大杀器:掌握collections与heapq,编写更高效的算法与数据处理

前言 在计算机科学的世界中&#xff0c;数据结构是构建强大和高效算法的基石。Python作为一门广泛应用的编程语言&#xff0c;以其丰富的数据结构模块为程序员提供了强大的工具。本文旨在深入研究Python的collections和heapq模块&#xff0c;通过更丰富的示例和详细的解释&…

竞赛 题目:基于FP-Growth的新闻挖掘算法系统的设计与实现

文章目录 0 前言1 项目背景2 算法架构3 FP-Growth算法原理3.1 FP树3.2 算法过程3.3 算法实现3.3.1 构建FP树 3.4 从FP树中挖掘频繁项集 4 系统设计展示5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于FP-Growth的新闻挖掘算法系统的设计与实现…

JavaScript 基本数据类型

字符串 在JS中&#xff0c;数据类型有&#xff1a;字符串、数字、布尔、数组、对象、Null、Undefined 用到最多的还是字符串和数组的转换。 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>首页</title><style&g…

Rust语言做数据抓取代码示例

这个任务需要使用到Rust语言和网络爬虫相关的库&#xff0c;以下是一个简单的示例代码。请注意&#xff0c;由于涉及到的具体问题和数据的复杂性&#xff0c;这个示例可能并不能直接满足你的需求&#xff0c;需要根据你的具体情况进行修改和扩展。 use reqwest; use serde::{De…

Splashtop 如何维护 GDPR 合规性

2018年&#xff0c;欧盟颁布了一项新法律&#xff0c;以保护欧洲公民的个人数据免遭任何收集数据的人不当处理。这可能意味着企业和组织&#xff0c;包括面对面和虚拟形式。这项开创性的法律为其他立法铺平了道路&#xff0c;例如加利福尼亚州的《加州消费者隐私法》&#xff0…

linux线程

文章目录 前言一、线程1、线程概念2、线程使用2.1 pthread_create2.2 线程共享数据和私有数据2.3 为什么线程切换的成本更低&#xff1f; 3、线程性质3.1 线程的优点3.2 线程的缺点3.3 线程异常3.4 线程用途 4、vfork接口 二、线程控制1、线程创建2、线程等待3、线程退出4、线程…

行业追踪,2023-11-13

自动复盘 2023-11-13 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

集成Line、Facebook、Twitter、Google、微信、QQ、微博、支付宝的三方登录sdk

下载地址&#xff1a; https://githubfast.com/anerg2046/sns_auth 安装方式建议使用composer进行安装 如果linux执行composer不方便的话&#xff0c;可以在本地新建个文件夹&#xff0c;然后执行上面的composer命令&#xff0c;把代码sdk和composer文件一起上传到项目适当位…

【STM32】定时器+基本定时器

一、定时器的基本概述 1.软件定时器原理 原来我们使用51单片机的时候&#xff0c;是通过一个__nop()__来进行延时 我们通过软件的方式来进行延时功能是不准确的&#xff0c;受到很多不确定因素。 2.定时器原理&#xff1a;计数之间的比值 因为使用软件延时受到影响&#xff0c…

Scala---介绍及安装使用

一、Scala介绍 1. 为什么学习Scala语言 Scala是基于JVM的语言&#xff0c;与java语言类似&#xff0c;Java语言是基于JVM的面向对象的语言。Scala也是基于JVM&#xff0c;同时支持面向对象和面向函数的编程语言。这里学习Scala语言的原因是后期我们会学习一个优秀的计算框架S…