初识C++之特殊类设计与类型转换

news2024/11/24 7:33:20

目录

一、特殊类设计

1. 设计一个对象不能被拷贝的类

2. 设计一个只能在堆上创建对象的类

2.1 禁止构造函数

2.2 禁止析构函数

3. 设计一个只能在栈上创建对象的类

4. 设计一个不能被继承的类

5. 设计一个只能创建出一个对象的类(单例模式)

5.1 单例模式的含义和作用

5.2 饿汉模式

5.3 懒汉模式

5.4 懒汉模式的线程安全问题

5.5 单例对象的释放问题

5.6 另一种懒汉模式的写法

二、类型转换

1.C中的类型转换

2. C++中的类型转换

2.1 static_cast

2.2 reinterpret_cast

2.3 const_cast

2.4 dynamic_cast


一、特殊类设计

特殊类设计,简而言之就是设计出一些具有特殊功能的类,例如禁止拷贝构造等。.

1. 设计一个对象不能被拷贝的类

要实现这个类很简单,在C++11之前,是采用“只声明不实现”并将其声明为私有的方式实现这个类。而在C++11之后就可以使用delete关键字删除类中的默认成员函数:

2. 设计一个只能在堆上创建对象的类

2.1 禁止构造函数

在一般情况下,一个类如果没有任何限制,其实是可以在栈区、堆区和静态区三个区域创建对象的:

那如果想让一个类只能在堆上创建对象,首先就要让这个类的对象不能在外部被随意使用。所以,第一步就是要封掉外部对构造函数的调用,即将构造函数声明为私有,然后单独提供一个函数提供对象创建。

但是要注意,因为要调用类的普通成员函数,就必须要有一个类。而这里不允许在外部创建类,只能通过类的成员函数创建,所以这个成员函数必须要为静态成员函数。类中静态成员函数的调用无需创建类。

尽管此时已经禁止了直接创建一个在栈上的对象,但是还是有方法间接在栈上创建对象的,那就是拷贝构造:

可以看到,虽然构造函数已经被封死,无法直接调用构造函数在栈上创建对象,但是却可以通过拷贝构造的方式实现在栈上创建对象。

要禁止通过拷贝构造的方式在栈上创建对象有几个方法。

(1)delete关键字

第一个方法就是用delete关键字删除拷贝构造函数:

2.2 禁止析构函数

如果不想类外部随意创建对象,将类的析构函数设置为私有也是一个可行的方法:

因为这是一个局部对象,出了作用域就要销毁。但是此时编译器无法找到该对象的析构函数,所以禁止了该对象的实例化。

在这种情况下,要创建对象就可以用new的方式创建。因为new的对象的生命周期是全局域。但是,因为析构函数是私有,所以虽然可以创建对象,却无法显式销毁:

 要解决这种情况也很简单,直接在类中提供一个调用析构函数的函数即可:

注意,在显式调用析构时,一定要加this,否则可能无法编译通过

当然,为了防止拷贝构造生成在栈上的对象,也可以将拷贝构造函数删除:

3. 设计一个只能在栈上创建对象的类

要实现这一目标,第一步就需要禁止在类外部随意调用构造函数,然后用类内部提供的函数创建对象:

但是这里并不能完全限制类的对象在栈上创建,这个类依然可以在静态区被创建:

这个问题是无法完全解决的。因为这里在栈上创建和在静态区创建都是利用了拷贝构造,如果禁掉拷贝构造,就无法创建对象;但如果不禁,就会出现上图的情况。

如果确实想让这个类无法在在静态区创建,就需要完全封死外部的创建,然后让使用者完全通过调用类的函数来进行操作:

当然,这种方式也是有缺陷的,那就是每次调用CreateObj()都会创建一个新的对象,所以每次调用的对象都是不同的对象

当然,可以用引用的方式接收对象,这样就可以使用同一对象了。但是要注意,引用接收时必须要加const

4. 设计一个不能被继承的类

要设计一个不能被继承的类很简单,在C++98中,是将父类的构造函数私有,这样子类就无法调用父类的构造函数,就无法继承:
 

而在C++11中,新提供了一个关键字“final”使用了该关键字的类将无法被继承

5. 设计一个只能创建出一个对象的类(单例模式)

只能创建一个对象的类,叫做“单例模式”。单例模式是类的设计模式中的一种。设计模式就好比是一本兵法,是一套被反复使用、多数人知晓、经过分类的代码设计经验的总结。

设计模式一共有23种,而单例模式,就是其中之一。在C++中,其实并不是那么喜欢谈设计模式,在实际使用的设计模式可能也只有几种。但是Java和C++不同,java非常喜欢谈设计模式,如果是学习java,可能就需要对绝大部分乃至全部设计模式都有所了解。

在以前学习C++时,大家都接触过“迭代器模式”和“配接器模式”。如果大家是学习C++,又想对其他设计模式有所涉猎,可以去看看“工厂模式”和“观察者模式”。这两个设计模式在一些C++的大型项目中可能有所使用。至于其他的设计模式,C++中就几乎很少使用了。

5.1 单例模式的含义和作用

一个类在全局只有一个对象,即单例模式。该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

在实际中,单例模式是有许多应用场景的。例如服务器的配置信息,该信息需要在整个服务器的开启周期内存在,但是又只需要一份。因此,这些配置数据就可以交由一个单例对象统一存储读取,当有对象需要这些信息时,直接从这个单例对象中读取即可。

要实现单例模式,一般有两种方式。

5.2 饿汉模式

饿汉模式,简单来讲,就是在程序完全启动进入main函数之前就创建一个单例对象,无论你是否要使用

在饿汉模式中只创建一个对象的方式,首先就是禁止在类外随意创建对象。所以先将构造函数设置为私有。然后再在这个类中声明一个类对象,通过声明的这个类对象实现创建对象。因为这个类对象为私有,所以要将其设置static静态成员,以实现在类外实例化该对象。

为了方便外部使用这个单例对象,所以可以在类中提供一个函数,返回这个单例对象的引用:

这就是一个简单的饿汉模式。

为了方便测试,写一个插入函数和一个打印函数:

再写出如下程序并运行:

可以看到,此时这里所使用的就是一个实例化对象。

但是,由于此处并未禁止拷贝构造,所以是可以通过拷贝构造实例化出不同的对象的:

此时就不再是同一个对象了。因此,在单例模式的饿汉模式中,要禁止拷贝构造

饿汉模式的优点:

饿汉模式的优点就是简单易懂。

饿汉模式的缺点:

(1)饿汉模式的单例对象因为需要在进入main函数之前创建并初始化,而进入main函数就是程序启动成功。所以就可能导致程序的启动速度比较慢。并且有些类可能在初始化时还需要调用网络或数据库,这就会进一步拖慢程序的启动速度。

(2)当存在多个单例对象时,难以控制单例对象的初始化顺序。如果是在同一个文件里面还好,先定义的先初始化,还比较好控制;但如果在不同文件中,就难以控制初始化顺序。并且如果在不同文件中的两个类有初始化的依赖关系,一个类的初始化依赖与另一个类,此时就可能出现问题。

5.3 懒汉模式

懒汉模式与饿汉模式相反,懒汉模式的单例对象并不是在main函数之前创建,而是什么时候需要就什么时候创建

懒汉模式和饿汉模式一样,都需要禁止在类外随意创建对象。所以要将构造函数声明为私有。但是与饿汉模式不同的是,懒汉模式不再是在类中创建一个对象,而是创建一个类指针。这个指针初始化为空,当其为空时,就表示没有创建对象,于是在类中new一个对象,让这个指针指向它;如果不为空,表示已经创建对象,直接返回即可:

懒汉模式的优点对比饿汉模式就很明显。首先,懒汉模式的对象是在main函数之中创建的,不会影响程序的启动。其次,懒汉模式可以通过调用函数的顺序来控制对象创建的顺序。

但是,懒汉模式存在一个问题,那就是“线程安全”问题。

5.4 懒汉模式的线程安全问题

懒汉模式在单线程情况下并没有什么问题,但一旦是多线程,就可能出现各种问题。如果不知道线程是什么,可以翻阅同一专栏的线程库或linux中的线程栏中的内容进行了解,这里就不再过多赘述。

由于这里是要通过new创建对象,如果有多个线程并发访问这里函数,就可能导致同时有多个线程通过判断进入创建对象的语句。例如线程A刚通过判断进入准备new对象,此时CPU将线程A调走,换线程B进来,由于线程A并没有创建对象,所以此时_pins还是空,于是线程B通过循环new了一个对象。当线程B创建完对象被切走后,线程A被切回来,它从创建对象的位置开始运行。于是线程A也创建了一个对象,并将这个对象返回。这时就会出现不同的线程new了不同的对象的情况,但是仅返回了一个对象的地址,造成内存泄漏。

要解决很简单,可以直接使用“锁”。通过锁来让线程并发访问临界资源

这个锁要定义为静态,因为返回对象的函数是静态函数。静态函数没有this指针,无法调用类中的普通成员变量。

修改获取对象的函数如下:

在C++中的锁其实是用类实现的,所以可以直接用类的方式调用上锁和解锁函数。但是上面的写法有一定的缺陷。在懒汉模式中,因为只需要创建一个对象,所以只会进入判断语句一次。但是上面的写法使得每次调用GetInstance函数时都要上锁解锁,其中不仅会有锁资源的消耗,还会使线程由并发变为串行,拖慢运行效率。

因此,可以通过再外面再套一层检查,即“双检查”的方式,使该程序仅在第一次创建对象时使用锁,其他情况下无需使用锁:

如果在未来遇到这种只需要保护第一次进入的情况,就可以使用“双检查”。

但是这里还有一个问题,那就是new是可能抛异常的。一旦new抛异常,它就可能直接跳到外部的catch中。此时就会导致程序运行不到解锁的地方,使得线程持有的锁未释放而形成死锁。

为了避免这种情况,我们可以采取异常捕获的方式处理:

但是这种方式写出来的代码并不好看。所以可以采取另一种方式,即“RAII”使锁的生命周期与作用域相绑定

此时,这个锁就是和它所属的作用域的生命周期一致,一旦离开该作用域,将自动调用析构函数进行解锁。

 在实际中,是不需要我们自己写这个LockGuard的,库中自带:

5.5 单例对象的释放问题

一般来讲,单例模式的单例对象的生命周期都是整个程序,所以不释放也没有问题,因为在程序结束时会自动回收。当然,如果单例对象在程序运行时就不需要了,就需要手动释放。

假设一个单例对象在程序结束后,需要将它的数据写入到文件中,就可以提供一个释放函数:

但是如果不想每次结束时时都自己去调用这个函数来写入数据和释放空间,而是向自动实现,就可以采用“内部类”的RAII:

由于GC是内部类,所以可以不受限制直接访问外部类的static成员。这里直接在析构函数中调用DelInstance()函数,然后在定义一个静态成员。该成员的生命周期属于全局域。当程序结束时会调用它的析构函数释放资源,而它的析构函数中又调用了DelInstance函数,进而实现自动调用。

5.6 另一种懒汉模式的写法

在C++11之后,又有了一种懒汉模式的写法,这种写法不再使用指针而是创建静态对象

静态变量只会在第一次调用的时候初始化。所以,这个函数只有在第一次调用时才会创建并初始化一个_sins对象。当再次调用时,不会再调用构造函数进行初始化。

我们在构造函数中打印一句话并写下如下代码进行测试:

运行程序:

只出现了一次打印,说明此处仅调用了一次构造函数来创建一个静态成员。且这个成员是在调用GetInstance函数后才创建,满足懒汉模式的条件。

但是,使用这种方式要小心,因为在C++11之前,这种方式是不能保证局部静态变量的初始化是线程安全的。在C++11之后才能保证。如果是某些对C++11支持的不太好的编译器,可能就会出现线程安全问题。所以,这种写法并不是通用的。

二、类型转换

1.C中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配或者返回值类型与接收返回值类型不一致时,就需要发生类型转化。在C中,一共有两种形式的类型转换:隐式类型转换显式类型转换

隐式类型转换:编译器在编译阶段自动进行,能转换则转换,不能转换就编译失败

 显式类型转换:需要用户自行处理

2. C++中的类型转换

虽然C中的类型转换方式简单,但是还存在不少缺点:

(1)隐式类型转换有些情况会出现如数据精度丢失等问题。并且这些问题有时候用户难以察觉。

(2)显示类型转换会将所有的情况融合在一起,代码不够清晰。

为了解决这些问题,在C++中,又提出了四种类型转换。分别是static_castreinterpret_castconst_castdynamic_cast。在以后遇到需要强制类型转换的场景下,最后都针对性的使用这四种类型转换。这几个类型转换的运用方式都是“类型转换<要转换的类型>(变量名)”

但是,这四种类型转换的方式并不是强制要求的,因为C++需要兼容C,所以在C++中依然可以使用C的转换方式。

2.1 static_cast

这种类型转换的方式主要用于相近类型的转换。例如char、int、double这类具有一定相似性,只是在精度、范围大小之类的地方有区别的类型。

例如下面的int转double,就可以使用static_cast:

 但是如果是指针转int,就无法使用:

 因为指针是地址,和double这种整型并没有什么关联性可言,所以无法转换。

2.2 reinterpret_cast

reinterpret_cast就是用于关联性很弱的类型的强制转换的。比如static_cast无法将指针转为整型,但是reinterpret_cast就可以:

但是,reinterpret_cast也不是所有类型都可以任意转换,例如int*转double:

2.3 const_cast

const_cast可以用于删除变量的常性,便于赋值。其实函数中const修饰的变量并不是存在常量区,而是存在栈中。因为常量区的数据是绝对不允许修改的。而const变量虽然不允许直接修改,但可以间接修改。更准确来讲,const修饰的变量不应该叫做“常量”,而应该叫做“常变量”

例如下图中的程序,就通过const_cast的强制转换修改const变量a的值:

但是,我们分别将a和*bp的值打印出来:

这就很奇怪了,程序能够正常运行,说明代码没有问题,bp确实修改了a的值。但是为什么打印出来a和*bp的值不一样呢?再打开调试面板对比:

此时可以看到更奇怪的现象,从调试面板来看,a确确实实被修改为了2,但是打印结果中的a却是1。造成这个现象的原因就是编译器优化。因为a是const修饰的,所以在这个程序中,编译器默认为变量a不会被修改。既然a不会被修改,为了提高效率,就没必要每次使用a时都从内存中读取。于是在运行这个程序时,a会被自动加载到CPU的寄存器中当需要使用a时,就从寄存器中读取,而不是从内存找那个读取。当然,在vs中就是采用“压栈”的方式,而不是放入寄存器。但导致的结果都是一样的,不同的只是从寄存器还是从压栈中获取数据。

但是,a在后面被我们通过间接的方式修改了值,此时CPU并不知道内存中的a已经被修改,继续从寄存器中读取a,而不是从内存中读取。这就导致了数据不一致的问题。

要解决这一问题很简单,加上“volatile”关键字即可。这个关键字是用于“总是从内存中获取数据”的。即带有这个关键字的变量,无论它是什么状态,当需要使用时都需要从内存中读取

将程序修改如下并运行:

此时的打印结果就是正常的。

C++单独将这个强制类型转换分出来就是为了警示使用者,使用这个类型转换是有风险的。因为C++是兼容C的,所以,C其实也是可以用显式类型转换去掉常性的:

2.4 dynamic_cast

dynamic_cast是C++单独拥有的类型转换方式。主要是为了处理父子类的指针/引用问题

dynamic_cast用于父类对象的指针/引用与子类对象的指针或引用的相互转换。这里涉及两种转换。

向上转换:子类对象指针/引用——>父类对象指针/引用。这种情况不需要转换。子类指针给父类指针赋值时,会发生切片,将只属于父类的部分拷贝给父类指针。

向下转换:父类对象指针/引用——>子类对象指针/引用(使用dynamic_cast转换是安全的)。因为父类中缺少子类的成员变量,所以当父类指针转为子类子类指针时,就可能导致越界访问出现错误。

例如如下的程序:

在这个程序里面,func函数的参数是一个父类指针,此时既可以传父类指针进去,也可以传子类指针进去。但是,在这里是将传进来的指针转为了子类指针。如果这个父类指针本身就指向一个子类对象,那还没有什么问题。但如果这个父类指针指向的是一个父类对象,就可能出问题。

将func函数修改如下:

在传入一个指向父类对象的父类指针的同时,修改子类和父类的成员变量。运行该程序:

此时就出现了报错。原因是“越界访问”。因为这里用一个子类指针接收了一个强转后的指向父类对象的指针,这个指针指向的数据中并没有子类的成员变量。只是通过强转让bptr误以为它有。所以当bpt去指定的地址处访问时,该地址并不属与它的访问范围,发生越界访问。

使用dynamic_cast进行转化:

此时程序依然会崩溃,但是可以发现,bptr的地址变为了0。这是因为dynamic_cast并不是直接进行转换,而是会先进行检查。如果检查结果是可以转换,就转换;如果不能转换,就返回0

由此,我们就可以通过转换后的指针的地址来判断是否转换成功:

程序正常运行,筛选掉了转换失败的情况。

但是要注意,dynamic_cast只能适用于父类中存在虚函数的父子类转换,如果不存在虚函数,dynamic_cast失效:

这个条件可以看成是一种规定,不必深究。只需要记住dynamic_cast的使用有这个条件即可。

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

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

相关文章

开源世界的尴尬时刻

作者按&#xff1a;开源世界里全都是一片祥和、欣欣向荣吗&#xff1f;全都是热情开放、无私奉献吗&#xff1f;那当然不是。本文罗列几起尴尬世界&#xff0c;目的是让大家对开源世界有一个更全面更具体的认识&#xff0c;让大家对底层开源人的生活的心态有一个更切实际的了解…

ORBBEC(奥比中光)AstraPro相机在ROS2下的标定与D2C(标定与配准)

文章目录 1.rgb、depth相机标定矫正1.1.标定rgb相机1.2.标定depth相机1.3.rgb、depth相机一起标定&#xff08;效果重复了&#xff0c;但是推荐使用&#xff09;1.4.取得标定结果1.4.1.得到的标定结果的意义 1.5.IR、RGB相机分别应用标定结果1.5.1.openCV应用标定结果1.5.2.ros…

mac m1安装V2rayU

我原先window上使用的是V2rayN。mac系统使用的是V2rayU。 1.首先下载V2rayU 下载地址&#xff1a;https://github.com/yanue/V2rayU/releases 2.将下载的文件拉到application 安装后&#xff0c;点击运行&#xff0c;此时会弹出安全警告&#xff0c;需要在「系统偏好设置-安…

第4章-动态规划

第4章-动态规划 总分&#xff1a;100分 得分&#xff1a;100.0分 10.0 分 1 . 多选题 中等 10分 有关0-1背包问题,用c[i][j]描述子问题:1...i共i个物品,背包容量为j的最优值(装入背包的最大价值),则其子问题为:1...i-1共i-1个物品,背包容量为j-w ix i,以下说法正确的是( AB…

TypeScript教程(N)

弱类型和强类型 编程语言&#xff0c;按照数据类型是否固定可分为 强类型语言 和 弱类型语言。 弱类型语言 弱类型语言&#xff1a;变量、函数参数和函数的返回值&#xff0c;是没有类型的&#xff0c;一个变量可以接收任何类型的数据&#xff0c;一个函数也可以返回任何类型…

大学生所面临的网络安全问题有哪些?

网络安全从本质上来讲就是网络上的信息安全&#xff0c;就是指网络系统中流动和保存的数据&#xff0c;不受到偶然的或者恶意的破坏、泄露、更改&#xff0c;系统连续正常的工作&#xff0c;网络服务不中断。从广义上来说&#xff0c;凡是涉及网络信息的保密性、完整性、可用性…

登顶Nature 正刊!百度生物计算用AI首次实现mRNA领域重大进展

1985年11月21日的《自然》封面&#xff0c;是一张来自中国的“地图”。这张地图是清代总兵陈伦炯所编撰《海国闻见录》中的插图&#xff0c;是中国人开始认知与探索世界的见证&#xff0c;而选用这张封面&#xff0c;是为了配合当期的特别文章《科技在中国》。 那时候&#xff…

4 ROS2节点参数基础

4 ROS2节点参数基础 4.1 ROS2节点参数介绍4.2 使用C/C实现对节点参数的增删改查4.2.1 创建C/C节点参数的服务端4.2.2 创建C/C节点参数客户端 4.3 使用Python实现对节点参数的增删改查4.3.1 创建Python节点参数的服务端4.3.2 创建Python节点参数客户端 4.4 ROS2节点参数小结 其他…

2008-2020年上市公司能源消耗数据

2008-2020年上市公司能耗数据/上市公司能源消耗数据 1、时间&#xff1a;2008-2020年 2、指标包括&#xff1a;上市公司ID、证券代码、证券简称、资源名称、消耗量、单位 EndDate [统计截止日期] - YYYY-12-31 InstitutionID [上市公司ID] - null Symbol [证券代码] - 交易所…

[架构之路-187]-《软考-系统分析师》-5-数据库系统 - 操作型数据库OLTP与分析型数据库OLAP比较

OLAP与OLTP的区别&#xff1f; OLTP(Online transaction processing) 在线/联机事务处理。典型的OLTP类操作都比较简单&#xff0c;主要是对数据库中的数据进行增删改查&#xff0c;操作主体一般是产品的用户。 OLAP(Online analytical processing): 指联机分析处理。通过分…

Linux网络编程:三次握手 四次挥手

1. 三次握手 建立TCP可靠连接&#xff0c;只能是客户端先发起。 &#xff08;1&#xff09;SYN标志位为1&#xff0c;表示请求建立连接&#xff1b;ACK标志位为1&#xff0c;表示确认收到对方报文。 &#xff08;2&#xff09;seq为数据包序列号&#xff0c;ack为确认序列号。…

计算机视觉的应用4-目标检测任务:利用Faster R-cnn+Resnet50+FPN模型对目标进行预测

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用4-目标检测任务&#xff0c;利用Faster RcnnResnet50FPN模型对目标进行预测&#xff0c;目标检测是计算机视觉三大任务中应用较为广泛的&#xff0c;Faster R-CNN 是一个著名的目标检测网络&#x…

or-tools 应用案例分析:复杂作业车间调度问题

作业调度问题是常见的线性规划(整数规划)问题&#xff0c;其中多个作业在多台机器上处理。每个作业由一系列任务组成&#xff0c;这些任务必须按给定的顺序执行&#xff0c;并且每个任务都必须在特定的机器上处理。如何有效的利用所有的机器在最短的时间内完成所有的作业任务&a…

神经形态处理和自搜索存储如何降低联邦机构的网络风险

组织在边缘处理的信息量呈爆炸式增长。对于联邦机构和军队来说尤其如此&#xff0c;它们从设备、建筑物、船舶、飞机等中的移动设备和传感器生成大量数据。 寻找有效的方法来管理、使用和保护这些数据具有挑战性。但是有一个有效且具有成本效益的解决方案。神经形态处理和自搜…

c#笔记-数组

数组 声明数组 数组是一种可以声明多个同类型变量的数据结构&#xff0c;能替你声明多个变量。 并且其中的值可以通过索引动态访问&#xff0c;可以搭配循环批量处理这些值。 数组类型的写法是&#xff0c;在目标类型后加上一对中括号。 数组值没有字面量&#xff0c;需要构…

海天注塑机KEBA系统数据采集

本文章只针对海天注塑机的KEBA系统&#xff0c;因为其他注塑机厂家也用KEBA系统&#xff0c;他们的采集方式可能不太一样&#xff0c;所以后续有时间我将写其他文章来解释&#xff08;默认你已经向海天采购了OPC组件&#xff09;。 一、采集原理 采集软件&#xff08;OPC cli…

electron+vue3全家桶+vite项目搭建【18】electron新建窗口时传递参数【url版】

文章目录 引入实现效果展示实现思路实现步骤1.调整主进程新建窗口的handle2.调整新建窗口函数3.封装url获取请求参数的工具 测试代码 引入 electronvue的项目中&#xff0c;我们通过传入页面路由来展开新的页面&#xff0c;就像vue-router的路由传参一样&#xff0c;我们可以利…

排序算法 — 桶排序

文章目录 桶排序介绍桶排序实现桶排序复杂度和稳定性桶排序复杂度桶排序稳定性 代码实现核心&总结 桶排序介绍 假设待排序的数组a中共有N个整数&#xff0c;并且已知数组a中数据的范围[0, MAX)。在桶排序时&#xff0c;创建容量为MAX的桶数组r&#xff0c;并将桶数组元素都…

【AUTOSAR】【通信安全】E2E

目录 一、概述 二、约束和假设 三、依赖模块 四、功能描述 4.1 开发错误 4.1.1 运行时错误 五、API接口 5.1 E2E Profile 接口 5.2 E2E其他接口 一、概述 该文档制定了PRS E2E协议的平台特定实现要求。这包括所使用的接口和数据类型。 E2E保护的概念假设在运行时应对…

探讨Redis缓存问题及解决方案:缓存穿透、缓存击穿、缓存雪崩与缓存预热(如何解决Redis缓存中的常见问题并提高应用性能)

Redis是一种非常流行的开源缓存系统&#xff0c;用于缓存数据以提高应用程序性能。但是&#xff0c;如果我们不注意一些缓存问题&#xff0c;Redis也可能会导致一些性能问题。在本文中&#xff0c;我们将探讨Redis中的一些常见缓存问题&#xff0c;并提供解决方案。 一、缓存穿…