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

c++重中之重:“换个龟壳继续套娃“:运算符重载等的学习

 

 

文章目录

  • 前言
  • 一.运算符重载
  • 二.const成员
  • 三.取地址重载
  • 总结

 


前言

上一期我们讲到类的6个默认构造函数中的拷贝构造函数,这一期我们继续往下讲,当然难点肯定是运算符重载了。


 

一、运算符重载

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

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

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

注意:

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

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

3.用于内置类型的运算符,其含义不能改变。例:内置的整形 + ,不能改变+的含义,比如把加法弄成乘法之类的。

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

5.有五个运算符是不能重载的。1 .*(点星) 2  ::(域名限定符)   3  sizeof    4  ?:(三目操作符) 5  . (成员访问操作符)

class Date
{
public:Date(int year = 10, int month = 10, int day = 10){_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,2,10);Date d2(2023,2,5);d2.print();return 0;
}

 像上面的d1和d2两个参数该如何比较大小呢?以前在C语言我们通常写一个函数是传两个日期的地址过去然后挨个用指针访问去比较最后返回布尔值,这样会非常的麻烦,而c++的运算符重载正好解决了这个问题。:

class Date
{
public:Date(int year = 10, int month = 10, int day = 10){_year = year;_month = month;_day = day;}void print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023,2,10);Date d2(2023,2,10);cout << (d1 == d2) << endl;return 0;
}

 b73573bb15e54fc49d16643fcf22e4a7.png

b23f10412c9b4eadb52588f6ed02512d.png 通过上面的代码和图片大家应该可以看到运算符重载对于自定义类型有多方便,而运算符重载的规则我们也讲过,那么写到类外什么样子呢?

(由于类外不可访问类内私有成员可以先将私有成员改为共用或者知道友元函数的用友元)

b0f8108e45284803a3b98a11cf9c946b.png

类外的区别就是多一个参数,因为在类内有this指针。 cout打印d1==d2加括号的原因是流插入操作符的优先级高于==。

符号>的重载:

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;}}

 715e47c783f949e99cd1b2f37a308302.png

符号<=的重载:

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

 我们可以发现运算符很多都是实现一两个其他的就可以复用了。小于等于不就是大于的取反吗,只需要知道类内函数有隐藏的参数this默认指向第一个操作数即可。

符号<:

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

 小于就是大于等于的取反。

符号>=:

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

 大于等于就是小于的取反。

符号!=:

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

 赋值运算符重载

赋值运算符的重载格式:

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

2.返回值类型T& ,返回引用可以提高返回的效率,有返回值的目的是为了实现连续赋值

3.检测自己给自己赋值(因为两个一样的变量再去赋值会消耗空间)

4.返回*this 要复合连续赋值的含义

注意:赋值运算符的重载并不是强制要求参数为&,这里要和拷贝构造区分,赋值运算符即使用传值调用也可以使用不会发生无穷调用因为在传自定义类型参数的时候先拷贝构造一个临时变量然后将这个变量赋值给变量即可。

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

 d4ddac9956ab4c6bb7c274895c74df25.png

 一般来说赋值给另一个对象是不需要返回值的,但是为了实现连续赋值那么就必须返回被赋值的那个变量,由于被赋值的对象不会被销毁,所以为了不调用拷贝构造函数浪费空间直接使用引用返回即可。如果有人写成d1= d1这样的代码,那么不就白白的浪费了空间吗,所以我们直接判断一下

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

this是左操作数的地址,&d是右操作数的地址,这样的好处就是即使赋值时出现操作数一样的情况也不会白白浪费空间。

注意:运算符的顺序不是从右往左也不是从左往右,这是要看操作符的结合性的。

比如: += 是从右往左开始的   + 是从左往右开始的

我们之前说过,类的6个默认函数即使我们不写编译器也会写一个默认的,那么默认的能完成赋值重载的任务吗?这个问题和拷贝赋值是一样的,对于内置类型编译器可以完成赋值,但是对于像栈那样需要开不同空间的必须我们手动去写一个赋值重载。

c03373ebfcbe484c9dceb7bddca036ef.png

 前面讲拷贝构造的时候忘记了一个细节,那就是向上图中红色框起来的也是拷贝构造,有些人会有疑问,这里不是用赋值重载了吗?其实并不是,赋值重载的调用是针对两个已经实例化好的或定义的对象,而像上图中d5还没有定义出来是在实例化的过程中,是用d1初始化d5,用一个对象初始化一个对象用的是拷贝构造。

下面我们利用运算符重载实现一个计算日期的小程序,小程序的功能包括:日期+天数,日期+=天数,日期-天数,日期-=天数,日期的前置++后置++,日期的前置--后置--,日期-日期相差多少天,下面先展示源代码然后我们一个函数一个函数讲解:

class Date
{
public:Date(int year = 2023, int month = 2, int day = 10){if (year > 0 && (month >= 1 && month <= 12) && (day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期不合法:" << endl;exit(-1);}}void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}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){return !(*this < d);}bool operator>(const Date& d){return !(*this < d) && !(*this == d);}bool operator<=(const Date& d){return !(*this > d);}bool operator!=(const Date& d){return !(*this == d);}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}int GetMonthDay(int year, int month){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;}else{return MonthArray[month];}}//日期 += 天数Date& operator+=(int day){if (day < 0){*this -= -day;return *this;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;}//日期 + 天数Date operator+(int day){Date tmp(*this);tmp += day;return tmp;}//前置++Date& operator++(){*this += 1;return *this;}//后置++Date operator++(int){Date tmp(*this);*this += 1;return tmp;}//日期 -= 天数Date& operator-=(int day){if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;}//日期 - 天数Date operator-(int day){Date tmp(*this);tmp -= day;return tmp;}//前置--Date& operator--(){*this -= 1;return *this;}//后置--//int参数 仅仅是为了占位,根前置重载区分Date operator--(int){Date tmp(*this);*this -= 1;return tmp;}//日期相减(得到的是天数)int operator-(const Date& d){Date Max = *this;Date Min = d;int flag = -1;if (*this < d){Max = d;Min = *this;flag = 1;}int n = 0;while (Max != Min){n++;Min++;}return n * flag;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 12, 3);Date d2(2023, 6, 4);//cout << (d1 > d2) << endl;/*d1 = --d2;d1.Print();d2.Print();*///d2.Print();//cout << (d1 - d2) << endl;d2 =d1 - -100;d2.Print();return 0;
}

 首先,我们在构造函数中初始化的时候要确保日期是合法的,不能出现月数小于0或者大于12的,并且天数要大于0小于当月最大天数,所以我们在构造函数中加了一个判断,当日期不合法时就输出"日期不合法"并且退出程序。

int GetMonthDay(int year, int month){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;}else{return MonthArray[month];}}

因为我们计算日期的时候必须知道每个月是多少天而且还有闰年二月是29天的情况,所以我们写了一个函数得到每个月的天数,数组有13个是因为数组是从0开始我们为了方便直接在第一个位置加一个0即可。

接下来我们讲解日期+=天数:

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

 f200449ddde44486beb7fbb5166b9385.png

在这里由于不确定要加多少天如果加1000天那么就要重复上图的步骤所以这是一个循环,当天数大于当月最大天数的时候就进入循环,需要注意的是当月数加到13就说明越界了要及时改为合法月数。因为+=就是会改变本身的值,并且在函数结束后日期也没有被销毁,所以我们采用传引用的方式减少拷贝构造的消耗。判断天数是否小于0是因为我们不知道有人会不会写成-数,如果是负数那就是-=一个正数。

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

 日期+天数那么是不会改变本身的,所以我们需要拷贝构造一个变量,这时候直接复用+=操作符即可,由于tmp是函数中的临时变量,函数结束就会销毁,所以不能采用传引用的方式。

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

 前置++是先++在使用,所以直接+1返回即可。

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

 后置++是先使用再++,也就是说我们必须用一个变量接收开始的日期,然后自己+1返回开始没有+1的那个值即可,由于这个值是临时变量所以只能用传值返回。需要注意的是,编译器区分前置++和后置++的点是后置++的参数有一个int,这个int是占位符没有实际作用,也不用写参数,写一个int即可。

//日期 -= 天数Date& operator-=(int day){if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;}

01633d9128e8403fa9ffeef207135c42.png  

 需要注意的是减去天数后如果大于0就说明本月的天数够用不需要向上个月借,等于0也需要借因为没有2月0日,为了让借到的天数是上个月的所以月份--后再加上借的天数,与+=同理都要判断day是否为负数,为负数就变成了+上一个正数,那为什么+和-我们没有判断呢?因为+和-我们是用+=和-=复用的。

//日期 - 天数Date operator-(int day){Date tmp(*this);tmp -= day;return tmp;}

 这里与+一样复用就可以。前置--和后置--也与++一样。

//日期相减(得到的是天数)int operator-(const Date& d){Date Max = *this;Date Min = d;int flag = -1;if (*this < d){Max = d;Min = *this;flag = 1;}int n = 0;while (Max != Min){n++;Min++;}return n * flag;}

日期相减实现起来也很简单,我们以之前的为负,以后的为正,先定义两个变量Max和Min来存放两个日期,我们默认是第一个日期大于第二个日期,当第一个大于第二个日期的时候就说明是之前的那么让flag为负,如果第一个日期小于第二个日期,就让flag为正。然后我们用n来记录天数,当两个日期不相等就进入循环,让n和小的那个日期自加直到相等我们就能计算出有多少天了。

那么我们每次调用函数去打印日期是不是不方便呢?能不能直接用cout打印日期呢?答案是可以的,我们通过重载<<运算符即可完成。我们现在类中声明然后再类外实现。

6064ae0d669b4323a5d7e71a647448af.png 

那么我们写了一个为什么不能调用呢? b6e62ec09ff04e9cba9f808ba157052b.png

 我们只能通过调用函数的方式去调用,和我们想的并不一样,这怎么办呢?我们用cout不能直接调用的原因是操作符的左边是左操作数,右边是右操作数,而我们常用的打印习惯是右操作数,那么我们先来看一下左操作数是否能正确调用:

bcacee1a61bb4101aedb277509e15eb4.png

 我们发现是可以正确调用的,但是很奇怪我们喜欢写到右边。解决这个问题之前我们要先知道运算符重载在类中第一个操作符是*this,而像我们那样的写法很明显out是右操作数了,我们要的是out去作为左操作数,想要让out成为左操作数将运算符重载写到外面了不就解决了吗,因为外面是没有*this的。

b60aab9d83c04ebc80596f723c580eb7.png

但是当我们写到定义在类外发现不能访问类内的成员了,这里的解决方式有多个,我们讲两个简单的即可,第一个将类内私有改为公有如下图:

9a47ed1037e541b78940a6b8ee0b30a3.png

第二个是用友元函数,我们将这个函数设为类的友元就可以访问类的所有成员了。

6e43dddbc8af4e16b51f36bd7a1783ca.png aff0748b87b9484194e6bbd1729be082.png

 f46b26264e5544a0ade02787822004b5.png

 为什么编译器的cout支持多个打印我们的不可以呢?这是因为我们没有返回值,我们应该要将out返回这样就能连续打印了,因为out出了作用域没有被销毁所以我们可以返回其引用。

09ae26e18ef54f48be4b891a56e38ae9.png 4c2c15e2291d46de8afbb4d631300e10.png

这样就解决了打印自定义类型的问题,接下来我们再重载一下cin,与cout一样只需要改一下参数即可。

49601924b86143b88bff80e7463e976a.png

5ee6ff12334b495a95d9160a666f6aa9.png 为什么输入的参数d我们不加const了呢?这是因为我们输入会改变const的值如果加了const就不能改变了。

在这里需要注意一下,类里面的短小函数,适合做内联的函数,直接是在类里面定义的。

const成员

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

class A
{
public:void Print(){cout << _a << endl;}
private:int _a;
};
int main()
{/*A aa;aa.Print();*/const A aa; //权限的放大aa.Print();return 0;
}

419325d00e9741ea89b0be8ef9ccea3a.png

在这里为什么会报错呢?因为aa的类型是const A* ,而传递给this指针后变成了A* ,所以这里是权限的放大,想要解决只需要给this指针也用const修饰,如下图:

ac6188a4b63441909d9c44a91bebe1e8.png

这里函数后面的const修饰的是this指针,让this指针变成了const A*。

1c65ac6bc2db4be3a0000be3e684a9ca.png而上图是权限的缩小,权限的缩小是没有问题的,调用Func函数从A*变成了const A*.

总结:内部不改变成员变量的成员函数,最好加上const,const对象和普通对象都可以调用,比如下面的代码:

#include <assert.h>
class Array
{
public:int& operator[](int i){assert(i < 10);return _a[i];}const int& operator[](int i) const{assert(i < 10);return _a[i];}private:int _a[10];int _size;
};
void Func(const Array& d)
{for (int i = 0; i < 10; i++){cout << d[i] << " ";}
}
int main()
{Array ay;for (int i = 0; i < 10; i++){ay[i] = i * 10;}for (int i = 0; i < 10; i++){cout << ay[i] << " ";}Func(ay);return 0;
}

取地址重载

取地址重载和赋值运算符重载一样都会由编译器自己生成,当然有需求也可以自己去写。

939165b7ce584f3c9f632f1309239443.png

本来自定义类型用运算符必须自己重载,但是赋值运算符和取地址重载编译器生成的就够用。

9d550cff008d408e805821ecead76595.png 当然如果我们不想让别人获取我们的地址我可可以返回一个假地址如上图所示

0a454e68b5bd492cb93fad09dc768b7d.png

以上就是取地址重载的用法,总之不是非常必要是不用去自己写取地址重载和const取地址重载的。 

 


总结

学会运算符重载是学习c++必备的技能,c++独特的就是自定义类型,而运算符重载可以解决自定义类型使用运算符的问题。

 

相关文章:

c++重中之重:“换个龟壳继续套娃“:运算符重载等的学习

文章目录 前言一.运算符重载二.const成员三.取地址重载总结前言 上一期我们讲到类的6个默认构造函数中的拷贝构造函数&#xff0c;这一期我们继续往下讲&#xff0c;当然难点肯定是运算符重载了。 一、运算符重载 运算符重载是c为了增强代码的可读性引入了运算符重载&#xf…...

RabbitMQ简单使用

这篇文章通过一个最简单的例子&#xff0c;让初学者能了解RabbitMQ如何完成生产消息和消息的。 所有的程序员在学习一门新技术的时候&#xff0c;都是从 Hello World 进入到Colorful World的&#xff0c;本节也将按照惯例&#xff0c;从HelloWorld开始&#xff0c;演示RabbitMQ…...

Lambda表达式

&#x1f44c; 棒棒有言&#xff1a;也许我一直照着别人的方向飞&#xff0c;可是这次&#xff0c;我想要用我的方式飞翔一次&#xff01;人生&#xff0c;既要淡&#xff0c;又要有味。凡事不必太在意&#xff0c;一切随缘&#xff0c;缘深多聚聚&#xff0c;缘浅随它去。凡事…...

JSON数据格式【学习记录】

JSON介绍 JSON&#xff08;JavaScript Objet Notation&#xff09;是一种轻量级的数据交换格式。它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。它采用一种键:值对的文本格式来存储和表示数据&#xff0c;在系统交换数据过程中常常被使用&#xff0c;是…...

LeetCode——1234. 替换子串得到平衡字符串

一、题目 有一个只含有 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符&#xff0c;且长度为 n 的字符串。 假如在该字符串中&#xff0c;这四个字符都恰好出现 n/4 次&#xff0c;那么它就是一个「平衡字符串」。 给你一个这样的字符串 s&#xff0c;请通过「替换一个子串」的方式&a…...

Web自动化测试——selenium篇(二)

文章目录一、浏览器相关操作二、键盘操作三、鼠标操作四、弹窗操作五、下拉框选择六、文件上传七、错误截图一、浏览器相关操作 浏览器窗口大小设置 driver.manage().window().maximize();//窗口最大化 driver.manage().window().minimize();//窗口最小化 driver.manage().wi…...

RK3399平台开发系列讲解(文件系统篇)虚拟文件系统的数据结构

🚀返回专栏总目录 文章目录 一、超级块二、挂载描述符三、文件系统类型四、索引节点五、目录项沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍虚拟文件系统的数据结构。 一、超级块 文件系统的第一块是超级块,用来描述文件系统的总体信息。当我们把文件系…...

企业财务管理升级,智慧税务和数据可视化打造新标准

一、引言在发展社会主义市场经济的过程中&#xff0c;税收承担着组织财政收入、调控经济、调节社会分配的职能。中国每年财政收入的90%以上来自税收&#xff0c;其地位和作用越来越重要&#xff0c;可称之为国家经济的“晴雨表”&#xff0c;有效进行税务管理、充分挖掘税务大数…...

JFET(结型场效应管)

JFET的结构示意图 参考&#xff1a;https://blog.csdn.net/weixin_45882303/article/details/106008695 下图是实际结构图&#xff0c; 下面是原理图和符号表示&#xff08;参考连接中的图片&#xff09; 分析 VGS 对电压id的控制&#xff08;固定VDS&#xff09; 当让D和…...

oceanbase部署--使用OBD部署obagent和promethous_grafana软件

obagent OBAgent 通常部署在 OBServer 节点上。OBAgent支持推、拉两种数据采集模式&#xff0c;可以满足不同的应用场景。 OBAgent默认支持的插件包括主机数据采集、OceanBase 数据库指标的采集、监控数据标签处理和 Prometheus 协议的 HTTP 服务。 1&#xff09;编辑 OBAgent …...

浏览器广告拦截插件| 浏览器搜索广告横飞怎么办

文章目录浏览器广告拦截插件| 浏览器搜索广告横飞怎么办一、效果二、安装浏览器广告拦截插件| 浏览器搜索广告横飞怎么办 浏览器广告横飞怎么办&#xff1f;今天教你一招解决&#xff01;很多小伙伴说自己用的浏览器总是有广告。 今天咱们就针对这个问题分享一个浏览器插件&a…...

Redis优化内存篇

【内存消耗】 场景&#xff1a;业务ID->图片ID&#xff08;KV:partnerId->objectId&#xff09;。 刚开始&#xff0c;我们保存了1亿张图片&#xff0c;大约用了6.4GB的内存。 随着图片数据量的不断增加&#xff0c;Redis变慢了。 新的认知&#xff1a;String类型并不是适…...

Vue原理解析

文章目录1. VUE的响应式原理1.1 ViewModel1.2 双向绑定的基本原理1.3 什么是响应性1.4 Vue 中的响应性是如何工作的2. Vue 渲染机制2.1 虚拟 DOM2.2 渲染管线2.3 带编译时信息的虚拟 DOM2.3.1 静态提升2.3.2 修补标记 Flags2.3.3 树结构打平2.3.4 对 SSR 激活的影响1. VUE的响应…...

C# Lambda表达式含义及各种写法

Lambda表达式在各个语言中的表达方式都不太相同&#xff0c;本文重点介绍C#的Lambda表达式。 首先&#xff0c;Lambda表达式就是一个匿名的方法/函数。 以下面的一个完整版作为例子&#xff0c;前面是参数&#xff0c;后面是返回值&#xff1a; 由于 Lambda表达式和委托常常一起…...

计算机组成原理:1. 计算机系统概论

更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录1.1 计算机系统简介1.1.1 计算机软硬件概念1.1.2 计算机的层次1.1.3计算机组成和计算机体系结构1.2 计算机的基本组成1.2.1 冯诺伊曼计算机的特点1.2.2 计算机的硬件框图1.2.3 计算机的工作步骤1.3 计…...

【c#】c#常用小技巧方法整理(5)—— 字符串操作类

1、GetStrArray(string str, char speater, bool toLower) 把字符串按照分隔符转换成 List 2、GetStrArray(string str) 把字符串转 按照, 分割 换为数据 3、GetArrayStr(List list, string speater) 把 List 按照分隔符组装成 string 4、GetArrayStr(List list) 得到数组列表以…...

用队列实现栈VS用栈实现队列

之前我们就讲过队列&#xff0c;栈的基础知识&#xff0c;笔者之前有过详细的介绍&#xff0c;感兴趣的可以根据笔者的个人主页进行查找&#xff1a;https://blog.csdn.net/weixin_64308540/?typelately225. 用队列实现栈请你仅使用两个队列实现一个后入先出&#xff08;LIFO&…...

MY2480-16P语音模块的使用

MY2480-16P语音模块的使用开发环境&#xff1a;STM32CUBEMXKEIL5辅助软件&#xff1a;串口助手、迅捷文字转语音一、MY2480-16P语音模块引脚图及引脚定义二、选择触发方式三、使用串口控制MY2480-16P语音模块四、模块使用指南开发环境&#xff1a;STM32CUBEMXKEIL5 辅助软件&a…...

I/O 多路复用

。新到来一个 TCP 连接&#xff0c;就需要分配一个进程或者线程&#xff0c;那么如果要达到 C10K&#xff0c;意味着要一台机器维护 1 万个连接&#xff0c;相当于要维护 1 万个进程/线程&#xff0c;操作系统就算死扛也是扛不住的。 一个进程虽然任一时刻只能处理一个请求&…...

2023 最新版网络安全保姆级指南,从0到1,建议收藏!

一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;且过渡到网络安全用到编程的用到的编程的关键点不多。一般人如果想要把编程学好再开始学习网络安全往…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...