C++入门:运算符重载及日期类的实现

目录

1.赋值运算符重载

1.1 运算符重载

1.2赋值运算符重载 

1.3引用作为返回参数☆☆

1.4深入赋值运算符重载

2.实现日期Date类

2.1类之间的运算符重载 

2.1.1相等

2.1.2小于 

2.1.3复用实现其他 

2.2类与整形之间的运算符重载

2.3单目操作符的重载

3. 流插入、流提取的重载

4.const成员(关注权限问题)

5.取地址操作符的重载

6.完整代码


(文章末尾有完整代码)

1.赋值运算符重载

1.1 运算符重载

C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字 operator 后面接需要重载的运算符符号(有点类似于将operator+符号名作为一个“函数名”)

operator+运算符构成函数名,任然用熟悉的Date类,示例如下:

bool operator<(const Date& dt1, const Date& dt2)
{
	if (dt1._year < dt2._year)
	{
		return true;
	}
	else if (dt1._year == dt2._year)
	{
		if (dt1._month < dt2._month)
		{
			return true;
		}
		else if (dt1._month == dt2._month)
		{
			return dt1._day < dt2._day;
		}
	}

	return false;
}

                           

(由于运算符优先级的问题,d1 和 d2之间需要打上括号)

............................

除了作比较,我们是否可以对一个两个日期作差,或者对日期++  --呢?

我们都能明白这是个什么意思,但是目前的编译器还不太明白,所以需要我们通过运算符重载来实现

各种运算的返回值: 

                                                                  

 注意:

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

重载好之后,也可以有很多种调用方法: 

                                               

很明显,转换调用更好用。


那么, 那些运算符需要重载呢?

一个类需要重载哪些运算符,是按照是否有重载价值和重载需求来判断的


 新的问题:

 上文中的重载部分的代码块中,我们都将类中的被private修饰的变量放开了,否则无法访问 

有三种解决方案:

1.提供成员的get和set(自己写一个成员函数)

2.友元,会在之后讲解

3.将operator对应的函数重载为成员函数 

此处我们着重说明第三种,如果在成员函数中写:

bool operator==(const Date& d1,const Date& d2){
     return d1.year==d2.year&&
            d1.month==d2.month&&
            d1.day==d2.day
};

那么就会出现报错,因为参数个数应该和操作数个数一致 ,==是一个双目操作符,而加上隐藏的this指针作为形参,一共有三个参数,明显不一致。

        

所以我们需要改造一下这个函数重载的写法:

class Date {
public:
	Date(int year = 1900, int month = 1, int day = 1);

	bool operator==(const Date& d) {
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

有两种调用办法: 

               

转换调用:

编译器会先在类中找,再在全局中找,如果找不到就会报错。

因此,如果类和全局中都有operator==,会优先使用类中的。

再在汇编角度看一下两种调用方法:

底层是一样的。

自然地,更多的使用转换调用。

注意:在转化调用中,假如有两个操作数,第一个操作数会作为this 第二个会作为参数。

在==中体现不多,但是在< > -中就有区别了。


1.2赋值运算符重载 

  赋值重载,即赋值拷贝,也就是将一个已经存在的对象赋值给另一个已经存在的对象。

两个对象都是已经存在的!因此不要与拷贝构造混淆。

//拷贝构造
Date d3=d1;
//赋值重载
Date d4(1999,11,9);
d1=d4;

同上写法:

                               

进一步的,为了支持连续的赋值:

d1=d2=d3;

 我们也可以在类中将赋值运算符写为:

Date operator=(const Date& d);

给重载的赋值运算符一个返回值,就像原本的整数间的赋值运算符一样。

                             


1.3引用作为返回参数☆☆

 为了更详细的学习、了解赋值运算符的重载,我们先学习一下引用作为返回参数的一些知识。

 传值返回会生成当前对象的拷贝,会拷贝一个临时对象来作为函数的返回值

Date func(){
    Date d(2024,4,14);
    return d;
}

而传引用返回不会去拷贝,但是这样的写法是不正确的。因为出函数的时候d就已经被销毁了 

Date& func(){
    Date d(2024,4,14);
    return d;
}

当返回对象作为一个局部对象或临时对象,出了函数作用域就会调用析构函数,因此也不能直接这样使用引用返回。 因此,传值返回虽然多拷贝一次,但是能正确返回。

可以用静态区变量解决这个问题。 

                                 

在函数返回值的接受处也应当注意(用静态区开辟后引用返回时):

               

对于这两种接受办法,右侧的ref在接受时还会再拷贝一次,左侧不会再拷贝。

但是如果使用传值返回,还要注意接收处的权限问题,需要加const来适应临时拷贝所具有的常性。

tips:所有的传值返回,传回的值都会拷贝给临时变量,而临时变量具有常性,因此大部分的传值返回都要注意临时变量的常性带来的权限问题。


        ▲分析四种经典的与引用有关的返回:

1. Date返回,Date接受:

                          

最纯粹、最简单的返回:用值返回,开新空间接受值,也是曾经各位同学用的最多的返回模式。其本质为:函数func返回的是d的临时拷贝,临时变量(临时拷贝)是存放在当前函数栈帧的,也就是说d的拷贝是存放在main函数中的。func返回了d的拷贝之后,ref1开辟出一个新的空间,在该新空间中拿到该临时变量的值(也就是将临时变量又拷贝到了ref1的新空间中去),因此,使用这样的用值返回、用值接受时,没有权限问题,func接受到的也是一组独立的完整的数据,不用担心销毁、覆盖一类的问题。


2.Date返回,Date&接受:

                      

 func返回的是d的临时拷贝,临时拷贝存储在main的函数栈帧中,并且具有常性,ref1直接作为该临时拷贝的别名,能修改和阅读该临时拷贝,属于权限的放大,因此报错。

加上一个const,就不会报错了:

                           

因为ref1是临时拷贝的别名,临时拷贝的生命周期与main一致,不会因func的销毁而被占用,所以此时的ref也是非常安全的,其内容是不会被覆盖的。


 3.Date& 返回,Date&接受

最危险,最容易出错的一种写法。

                                   

ref1相当于是d的引用、是d的别名。但,d是在func函数栈帧中的变量,函数栈帧销毁了,这一块空间是可以被覆盖的,数据与内容也是可以被清理的(取决于编译器),比如我们在

出了func空间之后再执行一个简单的fx()函数,就能观察到,ref1所代表的空间中的内容已经

被修改了:


 4.Date& 返回,Date 接受

                             

返回的是d的别名,并且ref1在一个新空间中通过d的别名拿到d的值(也就是通过d的别名将d的值拷贝给ref1).

d不是已经被销毁了吗?

由于编译器只是销毁了该栈帧空间(将该空间还给操作系统),但是d所对应的区域的值还没有被覆盖、改变,所以ref1成功拿到了d的值的拷贝。由于ref1中拿的是复制过的值,所以ref1中的数据也很安全,不会被覆盖。

 总而言之:

               

当然,引用返回在很多场合下可以减少拷贝,效率更高。

只要引用对象生命周期没有结束,就应该使用引用返回


1.4深入赋值运算符重载

回到刚才的赋值运算符重载的问题:

在类中实现赋值运算符重载时,就可以返回(*this)的引用,减少在返回时的拷贝次数。

(不用担心权限问题,例如d1=d2=d3;   其中的d1/d2/d3都是已经初始化好了的变量。d2=d3的返回值就是d2的引用,d2的引用作为d1赋值运算符的右操作数)

    

同时,如果赋值重载中涉及到使用深拷贝时,如果执行:

st1=st1;//栈中有malloc出的资源

浪费、消耗就非常大,所以我们再稍微处理一下:

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

 这样就更加完美了。


既然作为类中的默认成员函数,当我们没有显式实现运算符重载时,编译器会自动生成,性质与默认拷贝类似。需要深拷贝的,依然需要我们自主实现,编译器生成的只能实现浅拷贝

最后:赋值运算符重载不能实现在全局

提问:拷贝构造中也有“=”的写法,赋值运算符中也有“=”的写法,是不是实现一个就可以了呢? 

欢迎留言评论区。


2.实现日期Date类

我们以日期为例讲解了类和对象中的各种知识,现在我们来真正实现这个类

2.1类之间的运算符重载 

2.1.1相等

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

我们选择将函数的实现和声明分离(在Date.cpp文件中实现,在Date.h文件中声明) 

直接将函数定义在类中,默认其为inline函数;如上分开实现,则不会默认其为inline函数。

2.1.2小于 

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

在使用<时要注意,左操作数是this,右操作数是d

先传隐藏参数this,再传d,所以执行起来就是   *this<d 

2.1.3复用实现其他 

在实现了相等、大于、小于之后,当然可以通过直接cv逻辑再改符号,不过我们更加推荐复用的办法解决,这也是所有需要作比较的类在实现运算符重载时通用的实现思想

bool Date :: operator<=(const Date& d) {
	if (*this < d || *this == d)
		return true;
	return false;
}

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

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

注意:this是指针,需要对this解引用来获取该对象


2.2类与整形之间的运算符重载

之前我们说到,只要有一个操作数是自定义类型,就可以实现运算符重载

除了双目操作数,还有日期类与int(天数)作加减。竞赛中,类似的计算日期的题目经常作为签到题存在,我们通过加法计算进位的思想来实现他们,包括+=  -=  +  -

+= :

我们通过GetMonthDay来获得当年当月的天数(年份的目的主要是应对二月是否为闰月)

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

 实现GetMonthDay:

使用较频繁的函数直接放在该class对应的公共代码段作为inline函数,因为会多次调用,这样可以省去开栈帧的过程,例如此处的GetMonthDay,而之前的 opretator>= 等,由于使用相对较少,就可以放在.cpp文件中(定义和声明相分离),需要调用时编译器自动建立栈帧即可。

同理 -= - +

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

为了方便,我们任然采用复用的思维,但是此时的tmp是临时变量,由之前的知识得,不能再用引用作为返回值,而是需要执行一次拷贝,传值返回。

如果我们先实现+,也可以做到用+实现+=:

                              

+优先级更高,先调函数让*this和day作为参数进入函数,再将函数的返回值通过赋值运算符的重载赋值给*this.

想一想,用+复写+=更好,还是用+=复写+更好?

用+=复写 +更好,因为+的实现是传值返回,如果用+复写+=,明明不需要拷贝的的+=也会经历拷贝的过程。

对于-和-=,我们依然采用借位的办法:

                                                     

先在day上直接做减法,只要小于等于0就借位,注意借位借的是上一个月的天数。

Date& Date :: operator-= (int day) {
	this->_day -= day;
	while (_day <= 0) {//等于0也是不可以的,因为不存X月0号的说法
		_month--;
		if (_month == 0) {
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

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

此时的功能还不全面,如果一个日期+=-40,程序将出错。所以应当在两个被复用(+= -=)重载中加上判断。毕竟,重载的目的是增强代码可读性。

多个同一运算符重载可以构成函数重载,如下图(相同的运算符,不同的运算符参数,同样的函数名,不同的参数构成函数重载)

                              

2.3单目操作符的重载

以上两种-尚且存在不同的参数,若参数名等全部相同呢?

 //单目操作数
 //++d1   
 Date& operator++();
 //d1++
 Date& operator++();

         

这是一种规定,所有的类中的后置++和后置--,都用int来占位 

编译器的工程师会直接按照这种规定实现相关的映射,不要纠结于两种方法是如何联系的,为什么一个this在前面、一个this在后面等问题。

代码实现如下:

Date& Date::operator++() {
	//没有int,是前置
	*this += 1;
	return *this;
}

Date Date::operator++(int) {
	Date tmp = *this;
	++*this;
	return tmp;
}

 ▲:

后置++若还是使用的是Date&作返回,若使用Date接受还好(还是有风险,毕竟tmp对应的空间还给操作系统了),可以将数据拷贝进新的Date,倘若用Date&接受,就变成了上文中我们书写的最不安全 的一种写法。

所以,在自定义类型中 ,我们更推荐使用前置++和前置--,因为会减少拷贝的次数(拷贝了就需要开空间和析构,对于此处的日期类尚且还好,尤其是对于需要深拷贝的对象,代价就大得多了)


日期类作差:

因为我们已经实现了常规的+-等操作,所以直接让小日期作加法到达大日期,计数一共加减多少次即可。日期之间的数字相对计算机一秒钟上亿次的计算还是小问题。

int Date::operator-(const Date& d) {
	Date max = d;
	Date min = *this;
	if (max < min) {
		max = *this;
		min = d;
	}
	int ans = 0;
	while (min < max) {
		//习惯多用前置
		++min;
		++ans;
	}
	return ans;
}

(这个不是单目操作符,但是有了前文才能足够好的理解)

★补充:在这种多文件的项目中,如GetMonthDay这种成员函数直接实现是不会报错的(他被视作inline,会特殊处理,不进符号表,不用担心连接问题),否则会因为在test.cpp和Date.cpp中都被展开而报重复定义的错,我们通过静态区的方法解决:

                         


3. 流插入、流提取的重载

在之前(包括C语言阶段),如果我们想查看写好的类的内容,我们需要自主写一个Print函数来实现。

C语言不支持:

                                    

C++提供的符号重载,让我们有机会用流插入和流提取来按照我们的意愿打印一个对象。

为什么内置类型都能自动识别并且被输出?其本质也是重载  

内置类型都是提前被重载实现好了的:

为了兼容C语言,C++将cpp和c的输入和输出混合兼容,这也是为什么cpp的输出相对较慢:

                    

日期类型的流插入、流提取:

ostream(out_stream,也就是输出流)中有一个叫console_out(cout)类型,同理istream中有一个cin.

这样处理变量顺序会反,因为函数传参默认第一个参数是this ,使用起来就变成了

d.operator<<(cout),也就是d<<out 


因此,我们将其实现为全局重载,就可以自由控制参数顺序:

此时成员变量被private修饰,不能被访问

并且现在还无法连续输出:

                                   

从左向右写,每运行一次重载,就把这个运算符的值返回为ostream,也就是我们的cout(因为cout是作为引用被传入,out是cout的别名,传引用返回就相当于把cout返回回去了,能让d2继续和cout执行重载过后的<<)

现在通过友元解决private修饰问题:

我们在class中加入语句:

friend ostream& operator<<(ostream& out, Date& d);

“我是你的朋友,我能去你家玩”,这样,我们重载的<<就能访问该类中的元素。

友元函数声明可以放在共有或者私有中

ostream& operator<<(ostream& out,const Date& d) {
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

istream& operator>>(istream& in, Date& d) {
	cout << "请输入合法的年月日:" << endl;
	in >> d._year >> d._month >> d._day;
	if (!(d.CheckDate(d._year, d._month,d._day))) {
		cout << "日期非法" << endl;
	}
    return in;
}

4.const成员(关注权限问题)

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

将一个只读的数据传给一个可读可写的函数参数,也算是权限的放大

                   

d1是const Date ,传出去的是const Date* const this

Print的参数(this)是 Date* const this  

(因为this本身是不允许被修改的,所以Print中原本有个限制指针的const)

用函数名后置的const来解决:

this本身是Date* const this(指向的空间地址不能改变的指针),这样修饰之后就是const Date* const this(双const,不能改变指向,不能改变指向的内容)

    形参处不能写this,自然就无法在形参处对this进行修饰,所以后置的const是一种对逻辑不闭环打的补丁

因此,为了让我们实现的函数以及重载都能对被const修饰过的变量进行操作,我们可以给大部分的函数加上一个后缀的const(声明和定义处都要加),这样既能操作如上图的d1,也能操作如上图的d2(缩小函数的权限,让低权限和高权限的变量都能被使用).

当然,也不是所有的都适合使用后缀const:

比如+=  -=的重载就不能在函数名之后加const。 因为+=或则-=是需要对其对应的this作出修改的

答案为:不可以、可以、不可以、可以

5.取地址操作符的重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
意义不大,但是可以用来返回假地址,返回你希望对方得到的地址。

6.完整代码

//.h头文件
#pragma once
#include <iostream>
#include <cstdlib>
#include <assert.h>
using namespace std;

 static int month_day[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

class Date {
    friend ostream& operator<<(ostream& out,const Date& d);
    friend istream& operator>>(istream& in, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print() const;
    ~Date();
    bool CheckDate(int year, int month,int day)const;
    Date& operator=(const Date& d);

    bool operator==(const Date& d)const;
    bool operator!=(const Date& d)const;
    bool operator<(const Date& d)const;
    bool operator<=(const Date& d)const;
    bool operator>(const Date& d)const;
    bool operator>=(const Date& d)const;

    //常用的函数直接在类内部实现
    int GetMonthDay(int year, int month) const {
        assert(month <= 12 && year > 0);
        if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
            return 29;
        }
        return  month_day[month];
    }
    Date& operator+= (int day);
    Date operator+ (int day)const;
    Date& operator-= (int day);
    Date operator- (int day)const;

    //单目操作数
    //++d1   
    Date& operator++();
    //d1++
    Date operator++(int);

    //日期之间作差
    int operator-(const Date& d)const;
  

private:
	int _year;
	int _month;
	int _day;
};
//.cpp文件
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"

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

bool Date :: CheckDate(int year, int month,int day)const {
	if (year < 0 || month < 0 ||
		month >= 13 ||GetMonthDay(year, month) < day ||
		day < 0) {
		return false;
	}
	return true;
}


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

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

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

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

bool Date :: operator<=(const Date& d)const {
	if (*this < d || *this == d)
		return true;
	return false;
}

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

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


Date&  Date :: operator+=(int day) {
	this->_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)const {
	Date tmp = *this;
	tmp += day;
	return tmp;
}

Date& Date :: operator-= (int day) {
	this->_day -= day;
	while (_day <= 0) {//等于0也是不可以的,因为不存X月0号的说法
		_month--;
		if (_month == 0) {
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

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

Date& Date::operator++() {
	//没有int,是前置
	*this += 1;
	return *this;
}

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

int Date::operator-(const Date& d)const {
	Date max = d;
	Date min = *this;
	if (max < min) {
		max = *this;
		min = d;
	}
	int ans = 0;
	while (min < max) {
		//习惯多用前置
		++min;
		++ans;
	}
	return ans;
}



Date::~Date() {
	_year = -1;
	_month = -1;
	_day = -1;
}

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

ostream& operator<<(ostream& out,const Date& d) {
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

istream& operator>>(istream& in, Date& d) {
	cout << "请输入合法的年月日:" << endl;
	in >> d._year >> d._month >> d._day;
	if (!(d.CheckDate(d._year, d._month,d._day))) {
		cout << "日期非法" << endl;
	}
    return in;
}

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

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

相关文章

物联网在工业中的应用是什么?——青创智通

工业物联网解决方案-工业IOT-青创智通 物联网在工业中的应用已经日益广泛&#xff0c;它为企业带来了前所未有的机会和挑战。物联网技术通过连接各种设备和系统&#xff0c;实现了数据的实时采集、分析和优化&#xff0c;从而提高了生产效率、降低了成本并提升了企业的竞争力。…

如何实现视频扫码看的效果?视频二维码在线的制作技巧

现在分享视频很多人会采用生成二维码的方法&#xff0c;让其他人可以扫描二维码在手机上看视频&#xff0c;从而实现快速传播分享&#xff0c;有利于用户获取内容的便捷性。将视频存入云端服务器后&#xff0c;通过扫码调取视频内容&#xff0c;无需传统的方式将视频下载保存到…

CV | FSGS使用高斯喷溅的实时少样本视图合成论文详解与项目实现

本文是对论文的详解与项目实现。 Paper:2023.12.01_FSGS: Real-Time Few-Shot View Synthesis using Gaussian Splatting arxiv.org/pdf/2312.00451.pdf Code:VITA-Group/FSGS: "FSGS: Real-Time Few-Shot View Synthesis using Gaussian Splatting", Zehao Zhu, Zhi…

DMR数字对讲机模块的特性有哪些?该如何选择?

DMR828S是思为无线公司研发的一款性价比高的2W全功能数字对讲机模块&#xff0c;可以和市场上通用的模拟制式对讲机兼容&#xff0c;带有DMR TierII数字对讲机的功能&#xff0c;内置Moto AMBE 声码器。模块内部集成了微控制器、数字对讲芯片、射频功放以及音频功放等电路&…

QT打包发布

参考&#xff1a;QT项目打包成软件进行发布的三种方式_qt程序打包-CSDN博客 这里只研究前两种&#xff1a; ①小技巧加图标&#xff1a; &#xff08;1&#xff09;下载一个合适的图标文件 .ico 格式。 &#xff08;2&#xff09;将图标放在QT工程的根目录&#xff0c;然后在…

【尚硅谷】Git与GitLab的企业实战 学习笔记

目录 第1章 Git概述 1. 何为版本控制 2. 为什么需要版本控制 3. 版本控制工具 4. Git简史 5. Git工作机制 6. Git和代码托管中心 第2章 Git安装 第3章 Git常用命令 1. 设置用户签名 1.1 基本语法 1.2 案例实操 2. 初始化本地库 2.1 基本语法 2.2 案例实操 3. 查…

利用常量数组解码的方法

【题目描述】 把手放在键盘上时&#xff0c;稍不注意就会往右错一位。这样&#xff0c;输入Q会变成输入W&#xff0c;输入J会变成输入K等。键盘如图所示。 输入错位后敲出的几行字符串&#xff0c;输出打字员本来想打出的句子。 输入仅包含数字、空格、大写字母或标点符号&am…

2025中国国际储能大会暨展览会(简称“CIES”)

2025年第十五届中国国际储能大会暨展览会 2025 15th China International Energy StorageConference and Exhibition 时间&#xff1a;2025年3月23-26日 地点&#xff1a;杭州国际博览中心主办单位&#xff1a;中国化学与物理电源行业协会承办单位&#xff1a;中国化学与物理电…

Argo基础课程3-BGC-Argo数据质量控制

数据获取&#xff1a; Coriolis/France: https://data-argo.ifremer.fr/dac/ FNMOC/USA: https://usgodae.org/pub/outgoing/argo/dac/ Real-Time数据 24小时内发布&#xff0c;提交至自动质量控制平台进行标记和调整&#xff0c;“R” Delayed-mode数据 经过仔细浏览时间序列…

【Python初学指南】:从零开始学习Python烟花代码实战案例

1. Python入门基础 Python是一种高级编程语言&#xff0c;具有简单易学、功能强大的特点。通过安装Python环境&#xff0c;我们可以进行第一个Python程序的编写和运行。Python的入门基础对于初学者来说是非常重要的第一步&#xff0c;因为它奠定了后续学习和应用的基础。 在P…

哪种裤子穿起来舒服?夏季舒适透气的男装裤子分享

想必大家都会十分注重自己的外观形象&#xff0c;所以对选择的衣服上有不少要求。但是现在市面上却有很多质量不好的裤子&#xff0c;一些商家为了利润而使用一些舒适性差的面料。 那到底裤子哪个品牌的质量比较好&#xff1f;作为一名服装测评师&#xff0c;这些年测评过的品牌…

open Gauss 数据库-05 openGauss数据库备份恢复指导手册

发文章是为了证明自己真的掌握了一个知识&#xff0c;同时给他人带来帮助&#xff0c;如有问题&#xff0c;欢迎指正&#xff0c;祝大家万事胜意&#xff01; 目录 前言 openGauss数据库备份恢复 1 实验介绍 1.1 关于本实验 1.2 实验目的 2 实验前提 3 物理备份和恢复…

json diff patch

文件和图片的比对靠字符串 目录 流程 安装 效果 使用 自适应 数组&#xff1a;最长公共子序列(LCS) 数组中的对象&#xff0c;给定id&#xff0c;类似dom tree的比较 流程 安装 npm install jsondiffpatch import * as jsondiffpatch from jsondiffpatch; const jsond…

中电金信:夯实云原生时代的系统韧性建设——中电金信混沌工程金融业实践

IT系统建设在经历过单机、集中、分布式的演变历程后&#xff0c;系统运维演练、故障模拟测试的复杂度也不断提高。在复杂的分布式系统中&#xff0c;基础设施、应用平台都可能产生不可预知的故障&#xff0c;在不能确知故障根源的情况下&#xff0c;我们无法阻止故障的发生。更…

Transform结构

面试者经常会问transform这个模型&#xff0c;一个典型的seq2seq结构。 1 背景 试问几个问题&#xff0c;为什么提出了transform模型。RNN对于长时间序列&#xff08;超过40&#xff09;压缩到一个上下文向量中出现记忆退化现象&#xff0c;无法更好捕捉上下文信息。因此trans…

C语言知识(1) static修饰详解分享

1.前言 哈喽大家好啊&#xff0c;今天来给大家分享c中static的使用&#xff0c;希望能对大家有所帮助&#xff0c;请大家多多点赞&#xff0c;收藏支持我哦~ 2.正文 在讲解static之前&#xff0c;先给大家铺垫三个概念&#xff0c;方便大家理解。 2.1三则知识铺垫 2.1.1作…

写了一个 SRE 调试工具,类似一个小木马

远程操作机器有时会比较麻烦&#xff0c;我写了一个工具&#xff0c;主要功能&#xff1a;1.远程执行命令 2.上传下载文件。是一个 Web Server&#xff0c;通过 HTTP 请求来操作机器&#xff0c;类似一个小木马。当然&#xff0c;因为是一个 Web Server&#xff0c;所以也提供了…

批量人脸画口罩

网上参考的修改了一下&#xff0c;图片放在根目录&#xff0c;命名叫做1.png&#xff0c;批量人脸画口罩 这个程序的目的是为了解决人脸数据集中的特征点缺失的不足 # -*- coding: utf-8 -*- import os import numpy as np from PIL import Image, ImageFile__version__ 0.3…

短视频矩阵源头====技术文档交付

短视频矩阵源头技术文档交付 搭建短视频矩阵系统源码需要以下步骤&#xff1a; 1. 确定系统需求和功能&#xff1a;明确系统需要支持哪些功能&#xff0c;例如短视频的上传、存储、播放、分享、评论、点赞等。 2. 选择合适的编程语言和框架&#xff1a;根据需求选择合适的编程…

【python】描述性统计计算偏斜度和峭度

文章目录 1.编写计算偏斜度和峭度的函数。并用自己编写的函数计算课本23页的习题1.5数据的偏斜度和峭度。2.从1.5数据中随机抽取2个容量为20的样本&#xff0c;分别计算它们的平均数和标准差3.请绘制给定数据的频率分布直方图&#xff0c;计算数据的均值、标准差、偏斜度和峭度…
最新文章