当前位置: 首页 > news >正文

【C++从练气到飞升】05---运算符重载

 🎈个人主页:库库的里昂
收录专栏:C++从练气到飞升
🎉鸟欲高飞先振翅,人求上进先读书

目录

⛳️推荐

一、运算符重载的引用

二、运算符重载

三、赋值运算符重载

1 .赋值运算符重载格式:

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

3 .用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝:

四、日期类的实现

1. 重载关系运算符

重载<运算符:

重载==运算符:

重载<=运算符:

重载>运算符:

重载>=运算符:

重载!=运算符:

2. 完善日期类

获取每个月份的天数:

重载+=运算符:

重载+运算符:

为什么不用+=复用+:

重载-=运算符:

重载-运算符:

重载日期-日期:

重载前置++和后置++运算符:

重载前置--和后置--运算符:

>>和<<运算符:

重载<<运算符:

重载>>运算符:

五、const成员

const权限问题:

总结:

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


⛳️推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

一、运算符重载的引用

本章将以日期类为例进行展开叙述

通常比较两个操作数的大小,会写成下述方式:

int main()
{int i = 1, j = 2;i < j;return 0;
}

但是对于日期类采用上述方式会发生报错:

class Date
{
public:Date(int year = 2023, int month = 9, int day = 25){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 10, 1);Date d2(2022, 2, 15);d1.Print();d2.Print();d1 < d2;return 0;
}

🌟因为日期类是我们自己定义的,属于一种自定义类型,它的大小比较方式,编译器是不知道的,而像int等内置类型,这是原生语言定义的,知道规则来比较,可以直接用<、>去比较两个内置类型变量的大小,但自定义类型不可以哦

最常见的解决方式:

🌟创建一个函数来通过一 一对比年月日来实现日期类的比较,不过会出现关于私有的问题,这个函数是写在类外面的,意味着,日期类的成员变量是private私有的,在类外面就无法访问,所以在这个函数里面是访问不到类对象中的_year、_month、_day,所以x1._year等都是错误的会发生报错,要想实现该函数的功能,可以采用下面3种方法:

class Date
{
public:Date(int year = 2023, int month = 9, int day = 25){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
//private:int _year;int _month;int _day;
};
bool DateLess(const Date& x1, const Date& x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month&&x1._day<x2._day){return true;}else{return false;}
}
int main()
{Date d1(2023, 10, 1);Date d2(2022, 2, 15);d1.Print();d2.Print();DateLess(d1, d2);return 0;

🌟但是对于这种函数写法相比于直观的<、>来看显然不是很直观,对于不清楚的other来说,读代码是很困难的,所以就引入了运算符重载

二、运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

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

bool operator<(const Date& x1, const Date& x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month&&x1._day<x2._day){return true;}else{return false;}
}
int main()
{Date d1(2023, 10, 1);Date d2(2022, 2, 15);//cout<<d1<d2<<endl;至于为什么不写成cout<<d1<d2<<endl是因为<<的优先级高于<d1<d2与operator<(d1<d2)本质上都是调用运算符重载,所以两者写法是等价的只不是一个显示调用,一个没有显示调用cout << (d1 < d2) << endl;cout << (operator<(d1, d2)) << endl;return 0;
}

🌟上述代码就是对<运算符的一个重载,此时两个日期类对象就可以直接用<来比较大小,d1 < d2本质上就是调用运算符重载函数,此外,还需要解决一个问题:上面的运算符重载函数是写在类外面的,日期类的成员变量是private私有的,该运算符重载函数还是不能用。

🌟注意:

  • 🌏不能通过连接其他符号来创建新的操作符:比如operator@
  • 🌏重载操作符必须有一个自定义类型参数

(int x1, int x2)都是内置类型是不可以的规定必须有一个自定义类型的参数
也就是说只能对自定义类型进行重载,内置类型不可以
bool operator<(int x1, int x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day){return true;}else{return false;}
}
  • 🌏用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
  • 🌏作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
d1<d2两个操作数是不可以随意换位置的,左操作数就是第一个参数(this),右操作数就是第二个参数(d)
d1.operator<(d2)bool operator==(Date* this, const Date& d2)
这里需要注意的是,左操作数是this,指向调用函数的对象(和上面一个意思)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;}else{return false;}
}

🌟注意:当运算符重载函数写成类成员函数和在类外面定义调用的时候是不一样的

类成员函数
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;}else{return false;}
}
int main()
{Date d1(2023, 10, 1);Date d2(2022, 2, 15);cout << (d1 < d2) << endl;cout << (d1.operator<(d2)) << endl;成员函数---符合调用规则因为有一个是隐含的参数(d1.operator(d2))return 0;
}
——————————————————————————————————————————————————————————————————————————————————
类外面定义函数
bool operator<(const Date& x1, const Date& x2)
{if (x1._year < x2._year){return true;}else if (x1._year == x2._year && x1._month < x2._month){return true;}else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day){return true;}else{return false;}
}
int main()
{Date d1(2023, 10, 1);Date d2(2022, 2, 15);cout << (d1 < d2) << endl;cout << (operator<(d1, d2)) << endl;return 0;
}
  • 🌏不能改变操作符的操作数个数,一个操作符是几个操作数,那么重载的时候就有几个参数
  • 🌏(.* 和 *不一样 * 是可以重载的) 、 (域作用限定符 ::)、 sizeof 、(三目运算符?:) 、(对象变量取成员 .) 注意以上5个运算符不能重载。这个经常在笔试选择题中出现选择题。

三、赋值运算符重载

🌟 首先要区分赋值运算符和拷贝构造:

  • 赋值:两个已经存在的对象进行拷贝
  • 拷贝构造:一个已经存在的对象去初始化另一个对象
Date d1(2023, 10, 1);
Date d2(2023, 10, 7);
d1 = d2;     --->调用赋值运算符重载
Date d3 = d1;      --->调用拷贝构造函数;或者写成这种也是拷贝构造Date d3(d1);

1 .赋值运算符重载格式:

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
Date& operator=(const Data& d)
{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}

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)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}

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

3 .用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝:

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

四、日期类的实现

下面日期类的实现,是通过定义和声明分离来写的所以需要加Date::

1. 重载关系运算符

对于关系运算符有以下几种<、== 、<=、>、>=、!=,因为它们之间存在的逻辑关系,可以通过复用来实现,就比如:想要知道一个数a是否>另一个数b就可以通过判断a是否<=b来实现,所以只需要写一个小于和等于的逻辑之后的复用即可
重载<运算符:
bool Date::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;}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)
{return !(*this <= d);
}
重载>=运算符:
bool Date::operator>=(const Date& d)
{return !(*this < d);
}
重载!=运算符:
bool Date::operator!=(const Date& d)
{return !(*this == d);
}

2. 完善日期类

对于日期类的计算,若想知道100天以前或是100天以后是哪一天是非常有价值的,但是一个日期类型和一个整型是可以相加相减吗?当然是可以的,可以通过重载运算符来实现。

获取每个月份的天数:
int Date::GetMonthDay(int year, int month)
{static : GetMonthDay肯定会重复调用,每次调用还要定义一遍这个数组会有一定的消耗const static int monthArray[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 monthArray[month];   返回的是数组中这个值的临时拷贝
}

注意:对于2月是分闰年和平年的,但是其他月份是固定不变的,所以可以通过一个数组来存放每个月的天数,并且以下标作为每个月的月份所以不是monthArray[12],而是monthArray[13]。其中要把数组定义为静态数组目的是为了防止每次调用还要定义一遍这个数组会有一定的消耗。
此外对于判断闰年和平年这里,还要注意先把month==2写在最前面,因为只有2月是需要判断的

重载+=运算符:
Date& Date::operator+=(int day)
{if (day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){//月进位_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}

注意:对于if语句为什么复用了-=运算符是因为加一个负的天数,是算多少天以前的日期,所以当天数为负的时候,可以复用-=。

重载+运算符:

+运算符和+=运算符本质是一样的,所以不需要再写一遍,只需要+复用+=运算符就可以了,但是+=运算后等价于a=a+b,a也是会改变的,而+运算符运算后a是不会改变的

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

注意:要计算n+m的最终结果,n和m这两个数值计算后是不会改变的,所以一个日期加天数,原日期是不会改变的,而原日期也就是this指针指向的内容,所以也就是不能修改this指针指向的内容,对此要先利用拷贝构造函数创建一个和*this一模一样的对象tmp,在tmp的基础上去加天数。此外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);}return *this;
}
重载-运算符:
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}
重载日期-日期:
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (max < min){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}

计算的结果是两个日期之间的天数,所以返回值是int,要想知道两个日期之间相隔的天数,可以设置一个计数器n,让小日期一直加到大日期,就可以知道两个日期之间相隔的天数。

重载前置++和后置++运算符:

++d3 前置++,返回++之后的
++d3 —> d3.operator++()

———————————————————————————————————————————
d3++ 后置++,返回++之前的
++d3 —> d3.operator++(0) 这里加参数是为了区分前置++和后置++
加一个int参数,进行占位,跟前置++构成函数重载进行区分,本质后置++调用,编译器进行特殊处理

自定义类型用前置++比较好,后置++需要拷贝代价比较大
Date& Date:: operator++()
{*this += 1; 复用了+=return *this;
}
——————————————————————————————————————————————————————————————————————————————————
Date Date:: operator++(int)
{Date tmp(*this);拷贝构造*this += 1;return tmp;
}int main()
{Date d3(2023, 7, 27);下面两者等价不过是前者显式调用,对于后置++或者后置--显式调用需要传个参数不过并无卵用Date ret1 = d3.operator++(0);//Date ret1=d3--; ret1.Print();d3.Print();Date ret2 = ++d3;ret2.Print();d3.Print();return 0;
}
重载前置--和后置--运算符:
Date& Date:: operator--()
{*this -= 1;  复用了-=return *this;
}
————————————————————————————————————————————————————————————————————————————————
Date Date:: operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}int main()
{Date d3(2023, 7, 27);下面两者等价不过是前者显式调用,对于后置++或者后置--显式调用需要传个参数不过并无卵用Date ret1 = d3.operator--(0);//Date ret1=d3--; ret1.Print();d3.Print();Date ret2 = --d3;ret2.Print();d3.Print();return 0;
}
>>和<<运算符:

对于内置类型可以直接使用<<、>>,编译器可以根据数据的类型进行打印,本质上是因为库中进行运算符重载。但是对于自定义类型,编译器是不知道怎样打印的,所以要想使用<<打印(自定义类型)日期类是要对运算符<<进行重载

我们在使用C++进行输入输出的时候,会用到cin和cout,它们俩本质上都是对象,cin是istream类实例化的对象,cout是ostream类实例化的对象。

而<<、>>不用像C语言的printf和scanf那样,char对应%c,int对应%d,float对应%f,是因为运算符重载本质上是函数,对这些不同的内置类型,分别进行了封装,在运算符重载的基础上又实现了函数重载,所以<<、>>支持自动识别类型。

重载<<运算符:
Date.h
void operator<<(ostream& cout);//放在类里面的Date.cpp
void Date::operator<<(ostream& cout)//--->operator(Date* this,ostream& cout)
{cout << _year << "-" << _month << "-" << _day << endl;
}Test.cppcout << d1;
d1 << cout;//虽然可以运行,但是不符合使用习惯和价值

当在类中重载<<运算符会发现用cout<<d1打印是会发生报错的,这是因为对于一个双目运算符的重载,它的左操作数(cout)会传递给运算符重载函数的第一个形参(this),右操作数(d1)会传递给运算符重载函数的第二个形参(cout),对于类的成员函数,它的第一个形参是默认的this指针,该指针是日期类类型的指针,和cout的类型不匹(—>operator(Date* this,ostream& cout))。为了解决上述问题,可以写成d1<<cout,此时就相当于d1.operator(cout),会把d1的地址传给this指针,形参再用一个ostream类型的对象来接收cout,虽然可以运行,但是不符合使用习惯和价值。
🌟最好的解决方式:把<<重载成全局函数
把<<重载成全局函数,就不会有默认的this指针,同时,还可以根据需要设置形参的顺序,void operator<<(ostream& out,const Date& d),出了作用域cout还在,所以可以用引用返回。

Date.h
void operator<<(ostream& cout,const Date& d);//全局的Date.cpp
void operator<<(ostream& cout, const Date& d)
{cout << d._year << "-" << d._month << "-" << d._day << endl;
}Test.cpp
cout << d1;

把<<重载成全局函数是解决了参数问题,但是默认在该全局函数中是访问不到日期类中的私有成员变量,为了解决这个问题,可以把该运算符重载函数设置成友元函数

🌟友元声明:在日期类里面第一行加上一条下述声明,此时在该全局函数体就可以使用日期类中的私有成员变量。


friend void operator<<(ostream& cout, const Date& d);
此声明不受类中访问限定符的限制

🌟连续的<<

Date.h
ostream& operator<<(ostream& cout,const Date& d);Date.cpp
ostream& operator<<(ostream& cout, const Date& d)
{cout << d._year << "-" << d._month << "-" << d._day << endl;return cout;
}
Test.cpp
cout << d1 << d2 << endl;

对于上述cout<<d1,是一个函数调用,返回类型是void,但是对于连续的<<,cout<<d1<<d2<<endl;cout先是作为左操作数打印d1,左向右执行cout<<d1;返回void,后面的d2就会报错,所以要写成ostream& operator<<(ostream& cout, const Date& d),返回一个ostream类型的cout,接着cout作为左操作数再次函数调用打印d2
当然这里改了友元声明也要改

friend ostream& operator<<(ostream& cout, const Date& d);
重载>>运算符:
希望通过流插入往d里面写入数据,所以不能加const修饰。istream& operator>>(istream& cin, Date& d);

Date.h
istream& operator>>(istream& cin, Date& d);Date.cpp
istream& operator>>(istream& cin, Date& d)
{cin >> d._year >> d._month >> d._day;return cin;
}Test.cpp
cin >> d3;

🌟友元声明

friend istream& operator>>(istream& cin, Date& d);

C++中的流插入和流提取可以完美的支持自定义类型的输入输出

五、const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数( void Print() const ),实际修饰该成员函数隐含的this指针( void Print(const Date* this) ),表明在该成员函数中不能对类的任何成员进行修改。这样,不仅普通对象可以调用该成员函数(权限的缩小),const对象也能调用该成员函数(权限的平移)。也防止了权限的放大。

const权限问题:

#include<iostream>
using namespace std;
class Date
{
public:Date(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   //  ---> void Print(const Date* this){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 Test()
{Date d1(2022, 1, 13);d1.Print();const Date d2(2022, 1, 13);d2.Print();
}
int main()
{Test();return 0;
}

🌟权限不可以放大

总结:

对于关系运算符可以考虑加上const修饰,因为并不会改变对象本身,但是并不是所有的成员函数都要加const修饰,要修改对象成员变量的函数,例如:重载的+=、-=等,是不能加const修饰的,因为会修改成员本身,而成员函数中如果没有修改对象的成员变量,可以考虑加上const修饰,这样不仅普通对象可以调用该成员函数(权限的缩小),const对象也能调用该成员函数(权限的平移)

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

#include<iostream>
#include"Date.h"
using namespace std;
Date* Date::operator&()
{cout << "Date* operator&()" << endl;return this;
}
const Date* Date::operator&()const
{cout << "const Date* operator&()const" << endl;return this;return nullptr;  不想被取到有效地址可以这样写
}
int main()
{Date d1(2023,10,1);const Date d2(2023,10,1);cout << &d1 << endl;cout << endl;cout << &d2 << endl;return 0;
}

🌟总结:这两个取地址运算符重载函数,又构成函数重载。这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容

相关文章:

【C++从练气到飞升】05---运算符重载

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书。 目录 ⛳️推荐 一、运算符重载的引用 二、运算符重载 三、赋值运算符重载 1 .赋值运算符重载格式: 2 .赋值运算符只能重载成…...

[leetcode] 994. 腐烂的橘子

在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到单元格中没有…...

如何本地搭建群晖虚拟机并实现无quickconnect服务环境远程访问

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…...

[Java基础揉碎]final关键字

目录 介绍 在某些情况下&#xff0c;程序员可能有以下需求&#xff0c;就会使用到final final注意事项和讨论细节 1) final修饰的属性又叫常量&#xff0c;一般用XX_XX_XX来命名 2) final修饰的属性在定义时&#xff0c;必须赋初值&#xff0c;并且以后不能再修改&#…...

用OceanBase binlog service 轻松进行数据回滚

背景 在日常的数据库运维过程中&#xff0c;难免会遭遇数据误操作的情形&#xff0c;比如因疏忽而执行了非预期的delete或update操作&#xff0c;这时就需要进行数据回滚。如果在OceanBase中启用了回收站功能&#xff0c;并设置了合适的undo_retention&#xff0c;那么我们可以…...

【C++】学习记录--condition_variable 的使用

condition_variable使用步骤如下&#xff1a;创建一个condition_variable对象创建一个互斥锁mutex对象&#xff0c;用来保护共享资源的访问在需要等待条件变量的地方&#xff0c;使用unique_lock<mutec>对象锁定互斥锁并调用condition_variable::wait()、condition_varia…...

Linux之时间子系统(四): tick 层模块(periodic 和dynamic )

一、时间子系统的软件架构 二、tick 层模块的文件 tick-common.c tick-oneshot.c tick-sched.c tick-broadcast.c tick-broadcast-hrtimer.c 这三个文件属于tick device layer。 tick-common.c文件是periodic tick模块&#xff0c;用于管理周期性tick事件。 tick-oneshot.c文…...

Docker Command

小试牛刀 # 查看docker版本 docker -v docker --version # 查看帮助 docker --help # 永远的Hello World docker run hello-world镜像操作 查看本地已有的镜像 docker images -a :列出本地所有的镜像&#xff08;含中间映像层&#xff09; -q :只显示镜像ID --digests :显示…...

Linux系统部署Paperless-Ngx文档管理系统结合内网穿透实现公网访问

文章目录 1. 部署Paperless-ngx2. 本地访问Paperless-ngx3. Linux安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 Paperless-ngx是一个开源的文档管理系统&#xff0c;可以将物理文档转换成可搜索的在线档案&#xff0c;从而减少纸张的使用。它内置…...

6.shell case控制语句

case控制语句 1.什么是case case条件语句相当于多分支的if/elif/else条件语句&#xff0c;主要还是用来做条件判断的,常被应用于实现系统服务启动脚本。 case语句中&#xff0c;会将case获取的变量值与表达式部分的值1、值2、值3等逐个进行比较&#xff0c;如果变量值和某个表…...

如何判断HDMI接口版本是1.4还是2.0呢?

如何判断HDMI接口版本是1.4还是2.0呢&#xff1f; HDMI是一种用于传输高质量音频和视频信号的接口标准。随着技术的不断发展&#xff0c;HDMI接口也经历了多次升级和改进。在市场上&#xff0c;常见的HDMI接口版本包括1.4和2.0。判断HDMI接口版本主要通过以下几种方法&#xff…...

【开发环境搭建篇】NodeJS的安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…...

【Docker】docker和docker-compose一键安装脚本(linux)

一、准备和运行脚本 当前脚本下载的docker和docker-compose兼容系统架构为x64&#xff0c;可以根据自己实际系统版本更改下载链接 1. 在控制台使用vim新建: vim install-docker.sh2. 复制内容并粘贴&#xff1a; #!/usr/bin/env bash # 设置脚本在遇到错误时终止执行 set -…...

在 Windows 中安装配置并启动运行 Jenkins【图文详细教程】

安装 Jenkins 的系统要求&#xff1a; 最少 256MB 可用内存最少 1GB 可用磁盘空间JDK 8 / 11 /17&#xff08;Jenkins 是用 Java 写的&#xff0c;打包成 war 包&#xff09; 查看 JDK 的版本 Java JDK 在 Windows 中安装可以参考&#xff1a;https://www.yuque.com/u27599042/…...

C# 读取txt文本所有行

引用&#xff1a;System.IO; Path.Combine(); //将字符串组合成一个路径 Path.GetFullPath(); //返回指定路径的绝对路径 File.ReadAllLines(); //读取文本框返回一个数组 File.ReadAllText(); //读取文本框返回一个字符串 File.ReadAllBytes(); //读取文本框返回字节 …...

STM32使用常见错误合集(正在更新版)

本文章记录一些学习STM32的一些错误问题&#xff0c;师承江科大哈哈哈 一、编译、烧录类问题 1、烧录不成功&#xff0c;Keil提示RDDI-DAP Error【场景&#xff1a;PWM驱动直流电机】 解决方案&#xff1a;将电机断开再进行烧录&#xff0c;断开后就可以美美烧录不报错啦~ …...

Java Random类

一、Random类 在项目开发中&#xff0c;经常需要使用随机数值&#xff0c;例如&#xff0c;网站登录中的验证码&#xff0c;或者需要以一定概率实现的某种效果&#xff08;如游戏程序中的物品掉落等&#xff09;&#xff0c;就需要Java提供的Random类&#xff0c;该类用于生成…...

【Spring Cloud】微服务通信概述

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;人生乏味啊&#xff0c;我欲令之光怪陆离 本文封面由 凯楠&#x1f4f7; 友情赞助播出 目录 前言 1. Dubbo&#xff08;Spring Cloud Alibaba&#xff09;和 Spring Cloud 的适…...

MySQL的概述与安装

一、数据库的基本概念&#xff1a; 1.1 数据&#xff1a; 1&#xff09; 描述事物的符号记录称为数据&#xff08;Data&#xff09;。数字、文字、图形、图像、声音、档案记录等 都是数据。 2&#xff09;数据是以“记录”的形式按照统一的格式进行存储的&#xff0c;而不是…...

《被讨厌的勇气》书摘2

发生什么不重要&#xff0c;怎样看待才重要 生活的真谛就是活在当下 来自他人和社会的评价&#xff0c;造成了我们的骄傲和自卑。人们经常借“爱”之名&#xff0c;行控制之实 学会课题分离 当我们发现自己的价值的时候&#xff0c;才具备了让自己真正自由和自主的勇气 人…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...