【第三节】类的构造和析构函数

目录

一、数据成员的初始化

二、构造函数

2.1 什么是构造函数

2.2 构造函数的注意事项

三、析构函数

四、带参数的构造函数

五、缺省构造函数

六、构造函数初始化列表

七、拷贝构造函数和调用规则

八、深拷贝和浅拷贝

九、总结


一、数据成员的初始化

        定义普通变量,在定义时,一般都会给定一个初始值。

#include <iostream>
using namespace std;int g_Num= 100;  //全局变量
int main() {int nNumA = 30;//局部变量int *pNum=new int[20];//堆memset(pNum, 0, 20*sizeof(int));cout<<g_Num << nNumA;return 0;
}

        在以上的例子中,我们给局部变量和全局变量,堆内都定义了初始值。但是假如我们使用了更为复杂的类,用类定义对象该怎么办呢?如下所示:

//定义一个办公桌类
class CDesk {private:int m_high;int m_width;int m_length;int m_weight;
};//全局对象
CDesk g_objDesk; int main() {// 局部对象CDesk objDesk;CDesk* pobjDesk = new CDesk;//堆对象delete pobjDesk;return 0;
}

上面的都是没有初始值的,或许有人可能想到下面的方法。

class CDesk { //定义一个办公桌类
public://自己添加一个函数,能够给各个成员赋值。void SetValue() {m_high = 0;m_width = 0;m_length = 0;m_weight = 0;}private:int m_high;int m_width;int m_length;int m_weight;
};CDesk g_objDesk; //全局对象int main() {CDesk objDesk;  //局部对象objDesk.SetValue();objDesk.SetValue();CDesk* pobjDesk = new CDesk;//堆对象pobjDesk->SetValue();delete pobjDesk;return 0;
}

这样就显得非常的麻烦。有没有更好的方法呢?有的,就是使用下面学习的构造函数。

二、构造函数

2.1 什么是构造函数

        构造函数是一种独特的成员函数,其名称与类名完全一致,且不具备返回值类型,因此无法为其定义返回类型,亦不可使用void。在常规情况下,构造函数应被声明为公有函数。然而,将其声明为私有函数则有其特殊目的,例如在单例模式中使用。当创建类类型的新对象时,系统会自动调用相应的构造函数。

代码示例:

class CDesk { //定义一个办公桌类
public:CDesk() {cout << "我是构造函数"<<endl;}private:int m_high;int m_width;int m_length;int m_weight;
};CDesk g_objDesk; //全局对象int main() {CDesk objDesk;  //局部对象CDesk* pobjDesk = new CDesk;//堆对象delete pobjDesk;return 0;
}

运行结果:

        可以看到并没有人主动调用构造函数,但是这个函数自动被调用了。注意:全局对象局部对象,堆中的对象,只有有对象被创建了,都会自动调用构造函数。

2.2 构造函数的注意事项

问题一:构造函数仅用于初始化吗?
答案是否定的。构造函数是C++语言提供的一种机制,它在对象创建时自动被调用。在构造函数内部,可以编写任何代码,甚至可以模拟一个贪吃蛇游戏。然而,构造函数之所以具备这种自动调用的特性,主要是为了便于程序开发者进行对象的初始化工作。

问题二:构造函数能否在类外部定义?
确实可以。在类外部定义构造函数时,需要在函数名前加上“类名::”。构造函数没有返回类型,且不能被声明为虚函数。

示例代码:

class CDesk { //定义一个办公桌类
public:CDesk();private:int m_high;int m_width;int m_length;int m_weight;
};CDesk::CDesk() {//类外构造函数m_high = 0;m_width = 0;m_length = 0;m_weight = 0;
}CDesk g_objDesk; //全局对象int main() {CDesk objDesk;  //局部对象CDesk* pobjDesk = new CDesk;//堆对象delete pobjDesk;return 0;
}

问题三:main函数是第一个被调用的函数吗?
下面有了一个反例,全局对象的构造函数先于 main 函数执行。

class CDesk { //定义一个办公桌类
public:CDesk();private:int m_high;int m_width;int m_length;int m_weight;
};CDesk::CDesk() {//类外构造函数cout << "我是构造函数" << endl;m_high = 0;m_width = 0;m_length = 0;m_weight = 0;
}CDesk g_objDesk; //全局对象int main() {cout << "我是 main函数" << endl;CDesk objDesk;  //局部对象CDesk* pobjDesk = new CDesk;//堆对象delete pobjDesk;return 0;
}

运行截图:

问题四:如果一个类的成员是另一个类的对象,那么哪个构造函数会先被调用?
在对象创建过程中,如果一个类的数据成员是另一个类的对象,那么在调用构造函数时,首先会自动调用该数据成员对象的构造函数,随后再执行本类对象的构造函数。以下程序中的ClassRoom类(代表班级)包含学生类和老师类对象作为其成员,该程序展示了构造函数的调用顺序。

代码示例:

#include <iostream>
using namespace std;
class CStudent {
public:CStudent() {cout << "学生类构造函数" << endl;}
};class CTeacher {
public:CTeacher() {cout<< "老师类构造函数" << endl;}
};class ClassRoom {//假设这个班级中有3名学生一个老师
public:ClassRoom() {cout << "班级类构造函数" << endl;}
private:CStudent m_arrStr[3];CTeacher m_objTea;
};int main() {ClassRoom objClass;return 0;
}

运行结果:

可以看出,对象数据成员的构造函数也是按照定义顺序构建的。

问题五:new操作符与构造函数之间有何关联?
new操作符不仅负责分配内存,还会自动调用相应的构造函数。这一点在上文的示例中已得到体现。值得注意的是,虽然malloc函数同样能够分配堆内存,但它无法识别对象类型,因此不会调用构造函数。

问题六:构造函数能否带有参数?
确实可以。构造函数可以包含参数,并且一个类可以定义多个构造函数,通过参数的不同来实现函数重载。

三、析构函数

        除了构造函数之外,C++还引入了析构函数。当对象生命周期结束时,析构函数会自动被调用。它通常用于回收对象内部申请的资源,例如,如果对象中有一个指针指向堆内存,可以在析构函数中释放这部分内存。

析构函数的特点如下:
A. 没有返回类型;
B. 不接受任何参数;
C. 不应随意调用,尽管在特定情况下可以调用;
D. 不能进行重载(相比之下,构造函数可以有参数并支持重载);
E. 析构函数的功能与构造函数相对应,因此其名称是在构造函数名前加上逻辑非运算符“~”。

        当对象的生命周期结束时,例如在函数体内定义的局部对象,随着函数调用的结束,这些局部对象将被释放,此时会调用析构函数。

以下情况需要使用析构函数:
A. 如果构造函数打开了文件,在使用完毕后,需要通过析构函数关闭文件;
B. 如果从堆中分配了动态内存,在对象销毁前,必须通过析构函数释放这部分内存。

代码示例:

#include <iostream>
using namespace std;class CTest {
public:CTest() {//构造函数m_szName = new char[20];}~CTest() {//析构函数delete[] m_szName;}
private:char* m_szName;
};int main() {CTest objClass;return 0;
}

        该类定义的构造函数在对象之外分配一段堆内存空间,撤销时,由析构函数收回堆内存。注意,析构函数以调用构造函数相反的顺序被调用,请看如下示例:

#include <iostream>
using namespace std;class CMonitor {
public:CMonitor() { cout << "构造 显示器.\n"; }~CMonitor() { cout << "析构 显示器.\n"; }
};class CKeyboard {
public:CKeyboard() { cout<<"构造 键盘.\n"; }~CKeyboard() { cout<<"析构 键盘.\n"; }
};class CComputer {
public:CComputer() { cout << "构造 电脑.\n"; }~CComputer() { cout << "析构 电脑.\n"; }protected:CMonitor m_objMonitor;//数据成员是类对象CKeyboard m_objKeyboard;//数据成员是类对象
};int main() {CComputer* com = new CComputer();//显式调用无参构造delete com;return 0;
}

四、带参数的构造函数

        不带参数的构造函数不能完全满足初始化的要求,因为这样创建的类对象具有相同的初始化值,如果需要对类对象按不同特征初始化不同的值,应采用带参数的构造函数。如下面程序所示:

class CLocation {
public:CLocation(int nNumA, int nNumB) {m_X = nNumA;m_Y = nNumB;}int getx() {return m_X;}int gety() {return m_Y;}
private:int m_X, m_Y;//数据成员
};

一个类可以拥有多个构造函数构成重载,这样可以多样化的对对象进行初始化,请看如下示例代码:

#include <iostream>
using namespace std;class CLocation {
public:CLocation(int nNumA, int nNumB) {cout << "2个参数的构造函数" << endl;m_X = nNumA;m_Y = nNumB;}CLocation(int nNumA) {cout << "1个参数的构造函数" << endl;m_X = nNumA;m_Y = nNumA * 2;}int getx() {return m_X;}int gety() {return m_Y;}
private:int m_X, m_Y;//数据成员
};int main() {CLocation objA(1, 2);CLocation objB(3);return 0;
}

可以看出构造函数可以构成重载,根据初始化时传递的参数自动选择构造函数调用。

五、缺省构造函数

        C++语言规定,每个类都必须拥有至少一个构造函数。没有构造函数,就无法创建该类的任何对象。如果开发者没有为类定义构造函数,C++会提供一个默认的构造函数。这个默认构造函数是一个不带参数的构造函数,其作用仅限于创建对象,而不执行任何初始化操作。

        一旦类中定义了任何构造函数,C++将不再提供默认构造函数。如果需要无参数的构造函数,开发者必须自行定义。类似于变量的定义,当使用默认构造函数创建对象时,如果创建的是全局对象或静态对象,其成员数据将被初始化为0;而对于局部对象,其成员数据在创建时将是无意义的随机值。

        本节第一个例子中,创建的全局对象、静态对象及局部对象都是由编译器提供的缺省构造函数自动创建的,仅对成员数据分配了内存空间,未做初始化工作。一个类如果什么都没有则被称之为空类,一个空类的大小为1个字节,且编译器会为其隐式产生6个成员,假设有一个空类class Empty,则编译器会为其产生以下几个成员:

Empty();  //默认构造函数
Empty( const Empty&); //默认拷贝构造函数
~Empty(); //默认析构函数
Empty& operator=(constEmpty&); //默认赋值运算符
Empty* operator&(); //取址运算符
const Empty*operator&()const; //取址运算符const

六、构造函数初始化列表

        之前在构造函数中给变量初始值的时候,实际上都算不上初始化,在构造函数初始化列表中初始化才算的上是初始化,那么什么是构造函数初始化列表呢?

示例代码:

#include <iostream>
using namespace std;class CLocation {
public:CLocation(int nNumA, int nNumB) :m_X(nNumA), m_Y(nNumB) {cout << "2个参数的构造函数" << endl;m_X = nNumA;m_Y = nNumB;}CLocation(int nNumA) : m_X(nNumA), m_Y(nNumA * 2) {cout << "1个参数的构造函数" << endl;m_X = nNumA;m_Y = nNumA * 2;}int getx() {return m_X;}int gety() {return m_Y;}
private:int m_X, m_Y;//数据成员
};int main() {CLocation objA(1, 2); CLocation objB(3);
}

        如以上代码所示,在构造函数声明的后面,加上一个冒号,冒号后面就是初始化列表在初始化列表中,你可以为成员变量初始化。初始化时可以使用一个表达式。表达式可以有形参,也可以没有形参。

实际上,构造函数的执行过程可以细分为两个阶段:

  1. 初始化阶段:在这一阶段,内存分配的同时会直接填充数据。

  2. 普通计算阶段:这一阶段涉及构造函数体内的程序代码,任何在此阶段的初始化操作实际上是赋值操作。我们的初始化列表位于第一阶段,而在构造函数体内的赋值则属于第二阶段,此时不能称为初始化,而应视为赋值。

区分这两个阶段的重要性在于:

  1. const类型的成员必须在初始化列表中进行初始化,因为它们在定义后不能被修改。

  2. 引用类型的成员同样必须在初始化列表中初始化,因为引用一旦定义就必须立即绑定到一个对象。

  3. 如果类的成员是另一个类的对象,且该对象所属的类没有提供默认构造函数,那么这个对象成员也必须在构造函数的初始化列表中进行初始化。

这些成员必须在定义时立即初始化,因此它们需要在初始化列表中处理。

以下示例代码是const与引用的初始化问题:

#include <iostream>
using namespace std;class Object {
public:Object(int num = 0) :m_num(num), m_cNum(num), m_refNum(m_num) {//m_cNum=100;//常量不能在这里初始化//m_refNum = m_num;引用也不能再这里初始化cout << "Object"<< m_num << "..." <<endl;}~Object(){cout << "~Object " << m_num << "..." << endl;}void DisplaycNum(){cout << "cNum=" << m_cNum << endl;}private:int m_num;const int m_cNum;int& m_refNum;
};int main(void) {Object obj1(10);Object obj2(20);obj1.DisplaycNum();obj2.DisplaycNum();return 0;
}

以下代码是有参构造的对象成员的初始化问题

#include <iostream>
using namespace std;class Object {
public:Object(int num = 0) :m_num(num) {cout << "Object"<< m_num << "..." <<endl;}~Object(){cout << "~Object " << m_num << "..." << endl;}private:int m_num;
};class Container {
public:Container(int objl = 0, int obj2 = 0) {cout << "Container" << "..." << endl;}Container() {cout << "~Container ..." << endl;}private:Object m_objl;Object m_obj2;
};int main(){Container c(10, 20);return 0;
}

        在Container类的构造函数中,你声明了两个Object类型的成员变量m_objlm_obj2,但是在构造函数的初始化列表中并没有对它们进行初始化。在C++中,如果一个类包含了其他类的成员变量,那么这些成员变量必须在构造函数的初始化列表中进行初始化。

代码改成如下:

#include <iostream>
using namespace std;class Object {
public:Object(int num = 0) : m_num(num) {cout << "Object" << m_num << "..." << endl;}~Object() {cout << "~Object " << m_num << "..." << endl;}private:int m_num;
};class Container {
public:Container(int obj1 = 0, int obj2 = 0) : m_obj1(obj1), m_obj2(obj2) {cout << "Container" << "..." << endl;}private:Object m_obj1;Object m_obj2;
};int main() {Container c(10, 20);return 0;
}

根据上述实验结果,我们得出以下结论:

  1. 建议将所有初始化操作,包括普通数据成员和对象数据成员的初始化,放置在构造函数的初始化列表中进行。

  2. 对于那些没有默认构造函数的对象成员,其初始化必须且只能在构造函数的初始化列表中完成。一旦进入构造函数体内,再进行初始化则为时已晚。

  3. 这一原则同样适用于const型成员和引用型成员,它们都必须在初始化列表中进行初始化。

  4. 如果一个有参构造函数的所有参数都提供了默认值,那么它也可以被视为具有默认构造函数的功能。

  5. 对象成员的构造顺序是由它们在类中的定义顺序决定的,与初始化列表中的顺序无关。

七、拷贝构造函数和调用规则

        在C++中,提供了用一个对象值创建并初始化另一个对象的方法,完成该功能的是拷贝构造函数(也叫复制构造函数)。
拷贝构造函数的格式如下:
<类名>::<拷贝构造函数名>(<类名>&<引用名>)
{<函数体>}
例如:
CLocation::CLocation(CLocation &obj)
{   }

注意:
其中<拷贝构造函数名>与该类名相同
如果一个类中没有定义拷贝构造函数,则系统自动生成一个缺省拷贝构造函数,其功能是将已知对象的所有数据成员的值拷贝给对应对象的数据成员

拷贝构造函数的特点:
拷贝构造函数名字与类同名,没有返回类型
拷贝构造函数只有一个形参数,该参数是该类的对象的引用

代码示例:

#include <iostream>
using namespace std;class CLocation{
public://拷贝构造CLocation(CLocation& obj):m_X(obj.m_X),m_Y(obj.m_Y) {}//普通带参构造CLocation(int nX, int nY):m_X(nX), m_Y(nY){ }
private:int m_X, m_Y;};int main() {CLocation objA(1, 2);CLocation objB(objA);//此时调用的是拷贝构造函数return 0;
}

拷贝构造函数不仅用于通过已知对象的值创建同类型的新对象,还承担着另外两个关键角色:

A. 当对象作为函数参数传递时,系统会自动调用拷贝构造函数,以实现将对象的值复制给形式参数对象。

B. 当函数的返回值是对象时,系统会自动调用拷贝构造函数,创建一个临时对象来存储返回的对象值,然后将这个临时对象的值赋给接收函数返回值的目标对象。

拷贝构造函数调用示例:

#include <iostream>
using namespace std;class CLocation{
public://拷贝构造CLocation(CLocation& obj):m_X(obj.m_X),m_Y(obj.m_Y) {cout << "拷贝构造\n";}//普通带参构造CLocation(int nX, int nY):m_X(nX), m_Y(nY){ cout << "普通构造\n"; }
private:int m_X, m_Y;
};CLocation& fun_A(CLocation obj) { return obj; }
CLocation& fun_B(CLocation& obj) { return obj; }int main() {CLocation objA(3, 4);CLocation objB(objA);CLocation objC = fun_A(objB);CLocation& objD = fun_B(objB);return 0;
}

运行结果:

        程序输出结果说明程序中出现了三次调用拷贝构造函数:
1 以objA为蓝本创建objB
CLocation objB(objA);
2 实参objB对象被拷贝到形参
CLocation objC = fun_A(objB);
3函数返回时,调用拷贝构造函数,用对象obj创建一个临时对象保存obj的数据,在主函数中临时对象被释放前,将它的内容赋值到对象 objC中
return obj:
同时,可以发现,返回引用或者参数是引用,可以避免拷贝构造函数的调用。

八、深拷贝和浅拷贝

        如果不实现拷贝构造的话,系统也会帮助我们实现一个,功能就是挨个元素拷贝,这似乎和我们上面实现的功能是一致的。那么我们什么情况下需要自己实现拷贝呢?看下面的例子

#include <iostream>
#include <string.h>
using namespace std;class String {
public:String(const char* str = "");~String();void Display(); private:char* m_str;
};String::String(const char* str) {int len = strlen(str) + 1; m_str = new char[len];memset(m_str, 0, len);strcpy_s(m_str, len, str);
}String::~String(){delete[] m_str;
}void String::Display() {cout << m_str << endl;
}int main(){String s1("AAA");s1.Display();//系统提供的默认拷贝构造函数实施的是浅拷贝s2.m_str = s1.m_str;String s2(s1);//调用拷贝构造函数return 0;
}

        上述程序由于默认提供的拷贝构造函数仅仅是赋值,导致S1和S2的m_str 指向同一个位置,两个对象的字符串,有一个改变另一个也会改变。
另外两个对象的析构函数释放的是同一块位置,这也必然会出错,造成程序崩溃。这种拷贝称之为浅拷贝函数。

接下来我们自己实现一个拷贝构造函数

#include <iostream>
#include <cstring>using namespace std;class String {
public:String(const char* str = "");String(const String& other); //自己实现拷贝构造函数~String();void Display();private:char* m_str;
};String::String(const char* str) {int len = strlen(str) + 1;m_str = new char[len];strcpy_s(m_str, len, str);
}String::String(const String& other) {int len = strlen(other.m_str) + 1;m_str = new char[len];strcpy_s(m_str, len, other.m_str);
}String::~String() {delete[] m_str;
}void String::Display() {cout << m_str << endl;
}int main() {String s1("AAA");s1.Display();String s2(s1); // 调用拷贝构造函数s2.Display();return 0;
}

        在这个例子中,在 s2中又申请出了一块空间,将s1中的信息拷贝了进去。
这样,s1和s2就完全的没有关系了,把这种形式称之为深拷贝。可以看出,当类中有指针指向堆的时候,自己实现一个拷贝构造函数做成深拷贝是十分有必要的。

九、总结

        构造函数是一种特殊的成员函数,专门用于创建类的对象。它在对象创建时被调用,负责为对象分配内存空间,初始化其数据成员,并执行其他必要的资源请求操作。与之相对的是析构函数,它在对象生命周期结束时被调用,用于撤销对象并回收其占用的资源。构造函数和析构函数功能互补,通常成对出现。每个类的对象都必须通过构造函数来诞生,一个类可以定义一个或多个构造函数。编译器根据对象构造函数声明中形参的类型和数量与创建对象时提供的实参进行匹配,以确定应使用哪个构造函数,这一过程类似于普通重载函数的选择机制。当创建包含对象成员的类对象时,需要对这些对象成员进行初始化,此时会调用对象成员的构造函数。

        拷贝构造函数用于通过一个已存在的对象来创建一个新的对象,这种情况在对象作为参数传递或作为返回值返回时频繁发生。

        当类中未显式定义构造函数和析构函数时,编译器会提供默认的构造函数和析构函数。默认构造函数在创建对象时仅分配数据成员的存储空间,但不进行初始化;而默认析构函数在对象撤销时自动调用,负责回收资源。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于BP神经网络和小波变换特征提取的烟草香型分类算法matlab仿真,分为浓香型,清香型和中间香型

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................................................... …

深入探究Java自动拆箱与装箱的实现原理

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

Linux:进程控制(二.详细讲解进程程序替换)

上次讲了&#xff1a;Linux&#xff1a;进程地址空间、进程控制&#xff08;一.进程创建、进程终止、进程等待&#xff09; 文章目录 1.进程程序替换1.1概念1.2原理1.3使用一个exec 系列函数execl&#xff08;&#xff09;函数结论与细节 2.多进程时的程序替换3.其他几个exec系…

mysql连接池的实现

概要&#xff1a;本文介绍mysql连接池的实现&#xff0c;要求读者了解线程池 一、为什么需要mysql连接池&#xff1f; 资源复用 &#xff1a;不使用连接池&#xff0c;每次数据库请求都新建一条连接&#xff0c;将耗费系 统资源。 流程如下&#xff1a; 通过三次握手建立 TC…

大数据面试题 —— Hive

目录 Hive 是什么为什么要使用 HiveHive 的优缺点Hive的实现逻辑&#xff0c;为什么处理小表延迟比较高你可以说一下 HQL 转换为 MR 的任务流程吗 ***你可以说一下 hive 的元数据保存在哪里吗 ***Hive与传统数据库之间的区别Hive内部表和外部表的区别 ***hive 动态分区与静态分…

Hotcoin Research | 市场洞察:2024年5月13日-5月19日

加密货币市场表现 目前&#xff0c;加密货币总市值为1.32万亿&#xff0c;BTC占比54.41%。 本周行情呈现震荡上行的态势&#xff0c;BTC在5月15日-16日&#xff0c;有一波大的拉升&#xff0c;周末为震荡行情。BTC现价为67125美元。 上涨的主要原因&#xff1a;美国4月CPI为3…

效率工作:一键为多种资产添加统一材质(小插件)

1.需求分析&#xff1a; 当导入一批资产&#xff0c;或者有同一批结构体需要添加相同材质时&#xff0c;单独为每个模型都添加材质费时费力&#xff0c;有没有什么办法&#xff0c;能同时为多个资产添加材质。 2.操作实现 1.在网上找到了一款插件&#xff0c;经过验证&#xf…

如何将天猫内容保存为PDF格式?详细步骤与实战解析

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;保存天猫内容的重要性 二、环境准备与工具安装 1. 安装必要的Python包…

SpringCloud之SSO单点登录-基于Gateway和OAuth2的跨系统统一认证和鉴权详解

单点登录&#xff08;SSO&#xff09;是一种身份验证过程&#xff0c;允许用户通过一次登录访问多个系统。本文将深入解析单点登录的原理&#xff0c;并详细介绍如何在Spring Cloud环境中实现单点登录。通过具体的架构图和代码示例&#xff0c;我们将展示SSO的工作机制和优势&a…

HCIP-Datacom-ARST自选题库__BGP多选【22道题】

1.BGP认证可以防止非法路由器与BGP路由器建立邻居&#xff0c;BGP认证可以分为MD5认证和Keychain认证&#xff0c;请问以下哪些BGP报文会携带BCGP Keychain认证信息?(报头携带) open Update Notication Keepalive 2.传统的BGP-4只能管理IPv4单播路由信息&#xff0c;MP-B…

Spring-Cloud-OpenFeign源码解析-04-调用流程分析

在Spring-Cloud-OpenFeign源码解析-03-FeignClientFactoryBean分析到&#xff0c;通过Autowired或者Resource注入FeignClient实例的时候&#xff0c;实际上返回的是JDK动态代理对象&#xff0c;具体的实现逻辑在InvocationHandler的invoke方法中 回看ReflectiveFeign.newInsta…

AI大模型日报#0528:Greg专访 | 为什么OpenAI最先做出GPT-4、xAI获60亿美元融资、李飞飞经典对话Hinton

导读&#xff1a;AI大模型日报&#xff0c;爬虫LLM自动生成&#xff0c;一文览尽每日AI大模型要点资讯&#xff01;目前采用“文心一言”&#xff08;ERNIE 4.0&#xff09;、“零一万物”&#xff08;Yi-34B&#xff09;生成了今日要点以及每条资讯的摘要。欢迎阅读&#xff0…

YOLOv8/YOLOv7/YOLOv5+CRNN-车牌识别、车牌关键点定位、车牌检测(毕业设计)

目录 一、前言1、项目介绍2、图片测试效果展示 二、项目环境配置1、pytorch安装&#xff08;gpu版本和cpu版本的安装)2、pycocotools的安装3、其他包的安装 三、yolov8/yolov7/yolov5CRNN-中文车牌识别、车牌关键点定位、车牌检测算法1、yolov8算法介绍2、CRNN算法介绍3、算法流…

【加密与解密(第四版)】第十三章笔记

第十三章 HOOK技术 13.1 Hook概述 IAT HOOK&#xff08;改地址&#xff09; BOOL IAT_InstallHook(){BOOL bResult FALSE ;HMODULE hCurExe GetModuleHandle(NULL);PULONG_PTR pt ;ULONG_PTR OrginalAddr;bResult InstallModuleIATHook(hCurExe,"user32.dll",&qu…

韩顺平0基础学Java——第13天

p264-p284 安装IDEA&#xff0c;熟悉一下软件。 尴尬了&#xff0c;难道是这个版本的idea不支持jdk17&#xff0c;难受住了 成功了&#xff0c;顺便跑一下昨天的作业&#xff1a; 这都要跑2秒&#xff1f;是电脑的问题还是谁的问题&#xff1f;控制台里跑的好快的哦 设置id…

Thingsboard规则链:Message type switch节点详解

在物联网解决方案中&#xff0c;数据的高效处理与自动化决策流程是实现智能化管理的基础。Thingsboard&#xff0c;作为一个强大的开源物联网平台&#xff0c;通过其规则引擎为用户提供了一系列灵活的节点来定制复杂的业务逻辑。其中&#xff0c;Message Type Switch节点是构建…

BookxNote Pro 宝藏 PDF 笔记软件

一、简介 1、BookxNote Pro 是一款专为电子书阅读和学习笔记设计的软件&#xff0c;支持多种电子书格式&#xff0c;如PDF和EPUB&#xff0c;能够帮助用户高效地管理和阅读电子书籍&#xff0c;同时具备强大的笔记功能&#xff0c;允许用户对书籍内容进行标注、摘录和思维导图绘…

Springboot启动时报错Property ‘mapperLocations‘ was not specified.

这几天没整boot 晚上直接运行不了了 本想是在表现层写点代码测测接口的 localhost8080找半天 结果404 先考虑好久 是不是url输入错了 然后 就发现 结果boot都不能启动了 JUnit也测不出来 找了半天 结果是开关机导致数据库没开 手动打开服务 找到MySQL启动 IDEA连接数据…

ssm/springoot养老院问诊服务预约系统_96316老年人服务系统

2.管理员&#xff1a; &#xff08;1&#xff09;登入注册页面&#xff1a;管理员进行操作时需要是已注册登入的 &#xff08;2&#xff09;权限管理&#xff1a;管理员登入后可以运用权限进行相应的操作管理。 &#xff08;3&#xff09;用户管理&#xff1a;对用户进行删除、…

国产数据库替代加速 助力数字中国建设

5月24日&#xff0c;随着第七届数字中国建设峰会在福州的成功举办&#xff0c;释放数据要素价值、发展新质生产力成为当下热议的话题。 数据作为新型生产要素&#xff0c;是数字化、网络化、智能化的重要基础。北京人大金仓信息技术股份有限公司&#xff08;以下简称人大金仓&a…