二、类与对象(三)
17 初始化列表
17.1 初始化列表的引入
之前我们给成员进行初始化时,采用的是下面的这种方式:
class Date
{
public:Date(int year, int month, int day)//构造函数{_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是实际上并不能将其称为对对象中成员变量进行初始化,因为构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
例:
#include <iostream>
using namespace std;
class A
{
public:int _a1;int _a2;//const int _x;//const int _x = 1;
};
int main()
{A aa;return 0;
}
加const int _x;
前运行结果:
加const int _x;
后运行结果:
加const int _x = 1;
后运行结果:
为什么加了const int _x;
这条语句后运行起来编译器就报错了呢?这是因为const
变量必须在定义的位置初始化,而A aa;
是整体定义的地方,并不能在那里对_x
进行初始化,虽然我们可以将语句修改为const int _x = 1;
,但是这样做的实质并不是初始化,而是给了_x
一个缺省值,而且这个特性只有在C++11之后才有,那么在C++11之前该怎么办呢?所以说,必须给每个成员变量找一个定义的位置,不然像const
这样的成员将不好处理。
所以为了解决这样的问题,C++引入了初始化列表这样的方式。
17.2 初始化列表的特性
- 初始化列表由以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
例:
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
- 哪个对象调用构造函数,初始化列表就是它所有成员变量定义的位置。不管成员变量是否在初始化列表中显示,编译器都会对每个成员变量进行定义。
例:
#include <iostream>
using namespace std;
class A
{
public:A():_x(1),_a2(1){_a1++;_a2--;}void Print(){cout << _a1 << " " << _a2 << " " << _x << endl;}int _a1 = 2;int _a2 = 2;const int _x;
};
int main()
{A aa;aa.Print();return 0;
}
输出结果:
调试结果:
从调试结果可以看到,在上述例子中,当初始化列表对_a1
进行初始化时,由于_a1
未在初始化列表中显式设置,所以使用了缺省值对其进行初始化,而对_a2
、_x
则直接用( )
中的值进行初始化。等初始化完成后,初始化列表再去执行{ }
中的操作。
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
const
成员变量- 自定义类型成员变量(且该类没有默认构造函数时)
例:
#include <iostream>
using namespace std;
class A
{
public:A(int a)//不是A的默认构造函数:_a(1){}void Print(){cout << _a;}
private:int _a;
};
class B
{
public:B(int a = 1, int ref = 1): _aobj(1), _ref(ref), _n(10){}void Print(){_aobj.Print();cout << " " << _ref << " " << _n << endl;}
private:A _aobj; // 没有默认构造函数int& _ref; // 引用const int _n; // const
};int main()
{B bb;bb.Print();return 0;
}
初始化列表中无_aobj(1)
时运行结果:
初始化列表中无_ref(ref)
时运行结果:
初始化列表中无_n(10)
时运行结果:
正常运行结果:
结论:尽量使用初始化列表初始化,因为不管是否使用初始化列表,对于自定义类型的成员变量,一定会先使用初始化列表进行初始化。
- 每个成员变量在初始化列表中只能出现一次,因为只能初始化一次。
- 成员变量在类中声明的次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
例:
#include <iostream>
using namespace std;
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main()
{A aa(1);//构造函数aa.Print();//
}
输出结果:
从输出结果可以看到,由于_a2
在类中声明的次序在_a1
的前面,所以_a2
会比_a1
先初始化,而_a2
在初始化列表中又是用_a1
的值进行初始化,_a1
在没有被初始化之前又是随机值,所以_a2
初始化所得到的也是随机值。
18 explicit关键字
18.1 explicit关键字的引入
引入explicit
关键字之前,我们先来看下面一段代码:
#include <iostream>
using namespace std;
class A
{
public:A(int a):_a1(a){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main()
{A aa1(1);//构造函数A aa2 = 1;//编译能通过吗?aa1.Print();aa2.Print();return 0;
}
运行结果:
从输出结果可以看到,A aa2 = 1;
这条语句的左右两边明明不是同一个类型,编译却通过了,这是因为这里发生了隐式的类型转换。
也就是说,A aa2 = 1;
这条语句会先生成一个1
的具有常性的临时变量,将这个临时变量的类型转换为A
后再用来给aa2
初始化。
例:
#include <iostream>
using namespace std;
class A
{
public:A(int a):_a1(a){}void Print() const{cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main()
{A aa1(1);//构造函数const A& ref = 10;//类型转换//A& ref = 10;ref.Print();return 0;
}
用const
修饰A& ref
的运行结果:
不用const
修饰A& ref
的运行结果:
从输出结果可以看到,由于类型转换时生成的临时变量具有常性,如果被赋值的对象不具有常性的话编译器就会报错。
实际上,构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
如果不想让这种类型转换发生,就需要引入explicit
关键字。
18.2 explicit关键字的特性
- 对于单参构造函数,使用
explicit
修饰后将禁止类型转换。 - 对于第一个参数无默认值其余均有默认值的构造函数,使用
explicit
修饰后也将禁止类型转换。
例:
#include <iostream>
using namespace std;
class Date
{
public:// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用Date(int year):_year(year){}// 2. 虽然有多个参数,但是创建对象时后两个参数有默认值,使用explicit修饰,禁止类型转换/*explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}*/Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
void Test()
{Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用2023构造一个临时对象,而后用临时对象给d1对象进行赋值d1 = 2023;
}
int main()
{Test();return 0;
}
屏蔽单参构造函数后的运行结果:
屏蔽多参构造函数后的运行结果:
19 static成员
19.1 static成员的引入
引入static
成员之前,我们先来看一道面试题。
题目:实现一个类,计算程序中创建出了多少个类对象。
在我们学C语言的时候,可以通过定义一个全局变量count
来计数,但到了C++之后,如果还用这样的方式那么类的封装性就无法体现了,所以在C++中引入了static
成员来解决这个问题。
C++规定,声明为static
的类成员称为类的静态成员,用static
修饰的成员变量,称之为静态成员变量,用static
修饰的成员函数,称之为静态成员函数。
19.2 static成员的特性
- 静态成员变量一定要在类外进行定义和初始化,定义时不添加
static
关键字,在类中只起声明作用。 - 静态成员为所有类对象所共享,它不属于某个具体的对象,而是存放在静态区。
- 类静态成员的访问方式:
类名::静态成员
对象.静态成员
- 静态成员函数没有隐藏的
this
指针,不能访问任何非静态成员。 - 静态成员也是类的成员,受
public
、protected
、private
访问限定符的限制。
掌握了static
成员的特性,我们就可以用它来解答刚才的问题了。
#include <iostream>
using namespace std;
class A
{
public:A() { ++_scount; }A(const A& t) { ++_scount; }~A() { --_scount; }int GetACount() { return _scount; }
private:static int _scount;
};
int A::_scount = 0;
int main()
{A a1, a2;A a3(a1);A* ptr = nullptr;cout << a1.GetACount() << endl;cout << a2.GetACount() << endl;cout << ptr->GetACount() << endl;return 0;
}
运行结果:
从输出结果可以看到,不仅用对象.静态成员
的方式可以访问到静态成员,当对象的指针为空时也可以进行访问。
那类名::静态成员
这样的访问方式有什么应用场景呢?
我们可以考虑这样一个问题:当我们想知道一个函数内部创建了多少个对象时,该怎么做呢?
由于这个时候对象是在函数内部创建的,那么我们在函数外部再使用对象.静态成员
的方式进行访问就明显不合适了,那该怎么办呢?
有人提出了下面这种方法:
#include <iostream>
using namespace std;
class A
{
public:A(int a = 0) { ++_scount; }A(const A& t) { ++_scount; }int GetACount() { return _scount; }
private:static int _scount;
};
int A::_scount = 0;
void Test()
{A a1 = 1, a2 = 1;A a3(a1);
}
int main()
{Test();A a4;cout << a4.GetACount()-1 << endl;return 0;
}
运行结果:
从输出结果可以看到,这种方法通过在函数外再创建一个对象,然后用这个对象去访问静态成员之后再减1,就得到了函数内部所创建对象的个数。
这个方法虽然能够达到效果,但是总归是有点撇脚的,有没有什么更好的办法呢?
这个时候就可以考虑用类名::静态成员
的方式来进行访问。
采用这种方式的话,我们就需要用static
把GetACount
函数修饰为静态成员函数,由于静态成员函数没有this
指针,所以它就可以通过指定类域来进行调用。
#include <iostream>
using namespace std;
class A
{
public:A(int a = 0) { ++_scount; }A(const A& t) { ++_scount; }static int GetACount() { return _scount; }
private:static int _scount;
};
int A::_scount = 0;
void Test()
{A a1 = 1, a2 = 1;A a3(a1);
}
int main()
{Test();cout << A::GetACount() << endl;return 0;
}
运行结果:
19.3 练习
这道题本身其实不难,可是题目要求不能使用乘除法、for
、while
、if
、else
、switch
、case
等关键字及条件判断语句(A?B:C
),就让问题比较棘手了。
那么在这里,其实我们就可以利用static
的特性,通过在一个类里面声明静态成员变量_i
和_sum
,一个用于自增,一个用于求和。而后在类里面定义一个求和函数Sum
,让其实现每调用一次_sum
就加_i
,同时_i
自增以实现等差求和。
#include <iostream>
using namespace std;
class Sum
{
public:Sum(){_sum += _i;++_i;}static int GetSum()//用于获取私有成员_sum{return _sum;}
private:static int _i;static int _sum;
};
int Sum::_i = 1;
int Sum::_sum = 0;class Solution
{
public:int Sum_Solution(int n){Sum a[n];return Sum::GetSum();}
};
需要注意的是,由于部分老版的编译器不支持变长数组,所以在编译器上运行时可能会报错,但是在oj
上是可以正常通过的。
20 友元
20.1 友元的引入
之前我们想把一个日期类Date
输入,是采用这样的方式:
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day): _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, 5);d1.Print();return 0;
}
运行结果:
这样虽然能够实现输出的功能,但每次都要通过对象去调用Print
函数才能实现,有没有什么办法能够像内置类型那样直接用cout
输出呢?
有人想到如果能将流插入运算符<<
重载,那样就好办了,我们不妨来试一下:
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day << endl;return _cout;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023, 10, 5);cout << d1 << endl;return 0;
}
运行结果:
编译器报错了,这是什么原因呢?
实际上,这是因为cout
的输出流对象和隐含的this
指针在抢占第一个参数的位置,this
指针默认是第一个参数也就是左操作数,但是实际使用中cout
需要是第一个形参对象,如果要将operator<<
重载为成员函数,当前就只能通过下面的方式:
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}ostream& operator<<(ostream& _cout){_cout << _year << "-" << _month << "-" << _day << endl;return _cout;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023, 10, 5);d1 << cout << endl;// d1 << cout; -> d1.operator<<(&d1, cout);return 0;
}
运行结果:
虽然这样确实比刚才调用Print
函数要方便了,但这明显是不符合常规的调用逻辑的。
要让cout
是第一个形参对象,还有个方法就是将operator<<
重载成全局函数。
#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
//private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day << endl;return _cout;
}
int main()
{Date d1(2023, 10, 5);cout << d1 << endl;return 0;
}
运行结果:
这个时候好像就能满足我们的要求了,但是新的问题又出现了:类外要访问成员只能将成员变为公有,但这样一来封装性又无法得到保证了。
要解决这个问题,此时就需要友元来解决。
20.2 友元函数的特性
- 友元函数是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
- 友元函数可访问类的私有和保护成员,但不是类的成员函数。
- 友元函数可以在类定义的任何地方声明,不受类访问限定符的限制。
- 一个函数可以是多个类的友元函数。
- 友元函数的调用与普通函数的调用原理相同。
- 当模板函数作为类模板的友元函数时,不能像普通函数那样在类里面友元声明函数名即可,要直接在类模板中定义友元函数。
知道了以上友元函数的特性,我们不仅可以用cout
来输出自定义类型,还可以用cin
来输入自定义类型。
#include <iostream>
using namespace std;
class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}
int main()
{Date d;cin >> d;cout << d << endl;return 0;
}
运行结果:
20.3 友元类的特性
友元类的所有成员函数都可以是另一个类的友元函数,且都可以访问另一个类中的非公有成员。
关于友元类,有以下几点特性:
- 友元关系是单向的,不具有交换性。比如上述的
Date
类,如果我们还想再加一个Time
类,并在Time
类中声明Date
类为其友元类,那么可以在Date
类中直接访问Time
类的私有成员变量,但想在Time
类中访问Date
类中私有成员变量则不行。 - 友元关系不能传递。也就是说,如果
C
是B
的友元,B
是A
的友元,则不能说明C
是A
的友元。 - 友元关系不能继承。(该特性会在后续讲到继承的时候再详细介绍)
例:
#include <iostream>
using namespace std;
class Time
{friend class Date; // 声明日期类为时间类的友元类,则在Date类中就可以直接访问Time类中的私有成员变量friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};
class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "年" << d._month << "月" << d._day << "日"<< d._t._hour << "时" << d._t._minute << "分" << d._t._second << "秒" << endl;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;_cin >> d._t._hour;_cin >> d._t._minute;_cin >> d._t._second;return _cin;
}
int main()
{Date d1;cin >> d1;cout << d1;
}
运行结果:
相关文章:

二、类与对象(三)
17 初始化列表 17.1 初始化列表的引入 之前我们给成员进行初始化时,采用的是下面的这种方式: class Date { public:Date(int year, int month, int day)//构造函数{_year year;_month month;_day day;} private:int _year;int _month;int _day; };…...

CentOS 7 Tomcat服务的安装
前提 安装java https://blog.csdn.net/qq_36940806/article/details/134945175?spm1001.2014.3001.5501 1. 下载 wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.84/bin/apache-tomcat-9.0.84.tar.gzps: 可选择自己需要的版本下载安装https://mir…...

文件夹共享功能的配置 以及Windows server2012防火墙的配置
目录 一. 配置文件夹共享功能 1.1 为什么需要配置文件夹共享功能 1.2 配置文件夹共享 1.3 访问共享文件夹 1.4 配置取消 用户名和密码认证 二. windows server 2012防火墙配置 思维导图 一. 配置文件夹共享功能 1.1 为什么需要配置文件夹共享功能 我们在工作和生活中经…...

前端使用高德api的AMap.Autocomplete无效,使用AMap.Autocomplete报错
今天需要一个坐标拾取器,需要一个输入框输入模糊地址能筛选的功能 查看官方文档,有一个api可以直接满足我们的需求 AMap.Autocomplete 上代码 AMapLoader.load({"key": "你的key", // 申请好的Web端开发者Key,首次调…...

反转链表、链表的中间结点、合并两个有序链表(leetcode 一题多解)
一、反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 思路一:翻转单链表指针方向 这里解释一下三个指针的作用: n1࿱…...

深度学习中的Dropout
1 Dropout概述 1.1 什么是Dropout 在2012年,Hinton在其论文《Improving neural networks by preventing co-adaptation of feature detectors》中提出Dropout。当一个复杂的前馈神经网络被训练在小的数据集时,容易造成过拟合。为了防止过拟合ÿ…...
MySQL 中的 ibdata1 文件过大如何处理?
ibdata1 是什么文件? ibdata1 是InnoDB的共有表空间,默认情况下会把表空间存放在一个名叫 ibdata1的文件中,日积月累会使该文件越来越大。 ibdata1 文件过大的解决办法 使用独享表空间,将表空间分别单独存放。MySQL开启独享表空…...

Weblogic反序列化远程命令执行(CVE-2019-2725)
漏洞描述: CVE-2019-2725是一个Oracle weblogic反序列化远程命令执行漏洞,这个漏洞依旧是根据weblogic的xmldecoder反序列化漏洞,通过针对Oracle官网历年来的补丁构造payload来绕过。 复现过程: 1.访问ip:port 2.可…...
鸿蒙组件数据传递:ui传递、@prop、@link
鸿蒙组件数据传递方式有很多种,下面详细罗列一下: 注意: 文章内名词解释: 正向:父变子也变 逆向:子变父也变 **第一种:直接传递 - 特点:1、任何数据类型都可以传递 2、不能响应式…...

ubuntu 开机自报IP地址(用于无屏幕小车-远程连接)
目录 1.环境安装2.代码3.打包成可执行文件4.开启开机自启 1.环境安装 sudo apt-get install espeak #先安装这个库 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyttsx32.90 #再安装pyttsx3 pyinstaller pip install -i https://pypi.tuna.tsinghua.edu.cn/si…...
Angular——:host 和::deep
在Angular中,:host和::ng-deep是用于在组件样式中选择和修改宿主元素和子组件的特殊选择器。 :host是一个CSS伪类选择器,用于选择当前组件的宿主元素。它常用于在组件样式中应用样式到组件外部的宿主元素上。例如: :host {background-color:…...

键盘字符(#键)显示错误
当屏幕上显示的键与键盘上按下的键不同时,尤其是 # 键。大多数情况下,此错误是由于 raspbian 和 NOOBS 软件的默认英国键盘配置所致。 解决方案: 要解决此问题,您需要将配置更改为您自己的键盘或语言的配置。这可以通过转到树莓派…...

geemap学习笔记037:分析地理空间数据--坐标格网和渔网
前言 坐标格网(Coordinate Grid)简称“坐标网”,是按一定纵横坐标间距,在地图上划分的格网,坐标网是任何地图上不可缺少的要素之一。下面将详细介绍一下坐标格网和渔网。 1 导入库并显示地图 import ee import geem…...

Bluetooth Mesh 入门学习干货,参考Nordic资料(更新中)
蓝牙网状网络(Bluetooth mesh)概念 概述 蓝牙Mesh Profile | Bluetooth Technology Website规范(Mesh v1.1 后改名Mesh ProtocolMesh Protocol | Bluetooth Technology WebsiteMesh Protocol)是由蓝牙技术联盟(Bluetooth SIG)开…...

磁盘管理 :逻辑卷、磁盘配额
一 LVM可操作的对象:①完成的磁盘 ②完整的分区 PV 物理卷 VG 卷组 LV 逻辑卷 二 LVM逻辑卷管理的命令 三 建立LVM逻辑卷管理 虚拟设置-->一致下一步就行-->确认 echo "- - -" > /sys/class/scsi_host/host0/scan;echo "- -…...

GitHub教程-自定义个人页制作
GitHub是全球最大的代码托管平台,除了存放代码,它还允许用户个性化定制自己的主页,展示个人特色、技能和项目。本教程旨在向GitHub用户展示如何制作个性化主页,同时,介绍了GitHub Actions的应用,可以自动化…...

Frappe Charts:数据可视化的强大工具
一、产品简介: 一个简单、零依赖、响应式的 开源SVG 图表库。这个图表库无论是数据更新还是屏幕大小变化,都能快速响应并更新图表。数据生成和悬停查看都有舒服的交互动效,体验感很好。不仅支持配置颜色,外观定制也很方便。还支持…...

【Vulnhub 靶场】【Hms?: 1】【简单】【20210728】
1、环境介绍 靶场介绍:https://www.vulnhub.com/entry/hms-1,728/ 靶场下载:https://download.vulnhub.com/hms/niveK.ova 靶场难度:简单 发布日期:2021年07月28日 文件大小:2.9 GB 靶场作者:niveK 靶场系…...
浅谈C4模型
C4模型(C4 Model)是一种用于描述软件系统架构的轻量级模型,其目标是通过简化、清晰和易于理解的方式来表达系统的不同层次的架构信息。C4代表了“上下文”(Context)、“容器”(Container)、“组…...

SeaTunnel流处理同步MySQL数据至ClickHouse
ClickHouse是一种OLAP类型的列式数据库管理系统,ClickHouse完美的实现了OLAP和列式数据库的优势,因此在大数据量的分析处理应用中ClickHouse表现很优秀。 SeaTunnel是一个分布式、高性能、易扩展、用于海量数据同步和转化的数据集成平台。用户只需要配置…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...