【STL】list剖析及模拟实现
✍作者:阿润菜菜
📖专栏:C++
初识list
1. list基本概况
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素.
list实现的基本框架:
- 与string和vector不同,list的遍历需要通过迭代器来实现,list的迭代器也不再是原生指针,而是模拟原生指针行为的一层封装。
- 对于范围for的实现,本质就是通过迭代器,范围for可以遍历容器的迭代器,对迭代器进行解引用,然后依次拷贝给元素e,所以C++11的范围for没有什么新花样,本质上使用的还是迭代器实现的。
下面我们看看list的使用:
2.list常见的重要接口
先贴上官方文档:list文档介绍
一、 对于list的构造我们有四种构造方式:
构造空的list、构造含有n个值为val的元素初始化list、拷贝构造、区间(first,last)元素构造list。
void TestList1()
{list<int> l1; // 构造空的l1list<int> l2(4, 100); // l2中放4个值为100的元素list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3list<int> l4(l3); // 用l3拷贝构造l4// 以数组为迭代器区间构造l5int array[] = { 16,2,77,29 };list<int> l5(array, array + sizeof(array) / sizeof(int));// 列表格式初始化C++11list<int> l6{ 1,2,3,4,5 };// 用迭代器方式打印l5中的元素list<int>::iterator it = l5.begin();while (it != l5.end()){cout << *it << " ";++it;} cout << endl;// C++11范围for的方式遍历for (auto& e : l5)cout << e << " ";cout << endl;
}
二、ist迭代器的使用:
1.在使用上时可以将迭代器理解成一个指针,该指针指向list中的某个节点。同时提供了正向迭代器和反向迭代器。
2… 注意:begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{// 注意这里调用的是list的 begin() const,返回list的const_iterator对象for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it){cout << *it << " ";// *it = 10; 编译不通过}cout << endl;
}void TestList2()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));// 使用正向迭代器正向list中的元素// list<int>::iterator it = l.begin(); // C++98中语法auto it = l.begin(); // C++11之后推荐写法while (it != l.end()){cout << *it << " ";++it;}cout << endl;// 使用反向迭代器逆向打印list中的元素// list<int>::reverse_iterator rit = l.rbegin();auto rit = l.rbegin();while (rit != l.rend()){cout << *rit << " ";++rit;}cout << endl;
}
三、list的空间大小
四、list元素获取
五、list的元素操作
1.对于list来说,insert不会导致迭代器失效,vector存在迭代器失效是因为在扩容时reserve采取异地扩容的方式,这就导致原有迭代器指向了已经被释放的空间。
但list并不存在扩容这样的操作,list直接按需申请空间,你要插入多少个节点,那我就申请多少个节点,然后将所有的节点链接到哨兵节点前面就好了,所以insert之后迭代器依旧可以继续使用,因为他对应的节点空间不会被销毁,依旧好好的存在着。
2.对于list的erase来说就不一样了,erase会释放迭代器对应节点的空间,自然erase之后迭代器就会失效,如果想要继续使用迭代器,则可以利用erase的返回值来更新迭代器,erase会返回被删除节点的下一个节点的迭代器,我们可以用erase的返回值来更新迭代器
// list插入和删除
// push_back/pop_back/push_front/pop_front
void TestList3()
{int array[] = { 1, 2, 3 };list<int> L(array, array + sizeof(array) / sizeof(array[0]));// 在list的尾部插入4,头部插入0L.push_back(4);L.push_front(0);PrintList(L);// 删除list尾部节点和头部节点L.pop_back();L.pop_front();PrintList(L);
}// insert /erase
void TestList4()
{int array1[] = { 1, 2, 3 };list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));// 获取链表中第二个节点auto pos = ++L.begin();cout << *pos << endl;// 在pos前插入值为4的元素L.insert(pos, 4);PrintList(L);// 在pos前插入5个值为5的元素L.insert(pos, 5, 5);PrintList(L);// 在pos前插入[v.begin(), v.end)区间中的元素vector<int> v{ 7, 8, 9 };L.insert(pos, v.begin(), v.end());PrintList(L);// 删除pos位置上的元素L.erase(pos);PrintList(L);// 删除list中[begin, end)区间中的元素,即删除list中的所有元素L.erase(L.begin(), L.end());PrintList(L);
}
其实list还提供了一个sort函数,但因为效率很低,我们基本上不会用,对比同样的算法库函数sort,如果你要排序list,倒不如先将list的数据拷贝到vector进行排序,等排完序再将数据拷贝回list里面去,就算这样的排序的性能都是要比直接用list进行排序的性能要高不少 ---- 一定要release下进行对比,能差一倍!
测试代码:
#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
#include<time.h>#define N 100000int main()
{list<int> lt1,lt2;vector<int> v;for (int i = 0; i < N; ++i){auto e = rand();lt1.push_back(e);lt2.push_back(e);}//拷贝到vector进行排序int begin1 = clock();for (auto e : lt1){v.push_back(e);}sort(v.begin(), v.end()); //调用算法库sort进行排序size_t i = 0;for (auto& e : lt1) //将数据拷贝回去{e = v[i++];}int end1 = clock();int begin2 = clock();lt2.sort();int end2 = clock();printf("vector sort:%d\n", end1 - begin1);printf("list sort: %d\n", end2 - begin2);
}
算法库的sort底层用的是快速排序,为了key值选的合适,快排会进行三数取中,所以会进行迭代器的作差,而list的双向迭代器肯定不支持做差,所以调用算法库的sort就会报错。
如果想要排序链表,那就只能调用list类的成员函数sort来进行排序,list的sort底层用的是归并排序。
这里就要引出迭代器从功能上来说,可以分为三类:只能++的单向迭代器(单链表、哈希表),既能++也能 - - 的双向迭代器(list带头双向循环链表),既能++也能 - - 还能±某个具体的数的随机迭代器(string、vector)
resize用于调整链表的空间,如果是调整大一些,那就是一个一个的申请节点,尾插到链表上面去。如果是调整小一些,那也需要一个个的释放节点,相当于尾删节点。
// insert /erase
void TestList4()
{int array1[] = { 1, 2, 3 };list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));// 获取链表中第二个节点auto pos = ++L.begin();cout << *pos << endl;// 在pos前插入值为4的元素L.insert(pos, 4);PrintList(L);// 在pos前插入5个值为5的元素L.insert(pos, 5, 5);PrintList(L);// 在pos前插入[v.begin(), v.end)区间中的元素vector<int> v{ 7, 8, 9 };L.insert(pos, v.begin(), v.end());PrintList(L);// 删除pos位置上的元素L.erase(pos);PrintList(L);// 删除list中[begin, end)区间中的元素,即删除list中的所有元素L.erase(L.begin(), L.end());PrintList(L);
}// resize/swap/clear
void TestList5()
{// 用数组来构造listint array1[] = { 1, 2, 3 };list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));PrintList(l1);// 交换l1和l2中的元素list<int> l2;l1.swap(l2);PrintList(l1);PrintList(l2);// 将l2中的元素清空l2.clear();cout << l2.size() << endl;
}
list的模拟实现
list的迭代器 — 精华部分
迭代器的价值是什么:
a.迭代器对底层的实现进行封装,不暴露底层实现的细节。
b.提供统一的访问方式,降低使用者的使用成本。
- 实现list的迭代器,最底层其实就是一个链表节点结构体的指针,我们知道 迭代器: 要么就是原生指针,要么就是自定义类型对原生指针的封装,本质都是模拟指针的行为! 所以对list要实现迭代器的功能,我们对这个指针进行了封装,所以list的迭代器就是用一个自定义类型来封装 node* 。
- 我们知道对于模板实例化,加上模板参数才是类型,否则就是类名。
在实现const版本迭代器时最容易想到的一种方式就是重新构建一个类,类里面原有成员函数都不变,仅仅是将类名做出修改,然后我们把解引用函数的返回值修改成常引用,在使用const_iterator迭代器类型的时候,如果你解引用迭代器则直接调用__list_const_iterator类里面的返回常引用的重载函数。 - == 这样显然是代码冗余的,大佬是怎么做的?其实是通过增加模板参数,在传参数时,根据参数类型的不同实例化出不同的类。==
例如用const_iterator时,就是用第二个模板参数为const T&的类模板,等T类型确定时,就会实例化出具体的类,当用iterator时,我们就用第二个模板参数为T&的类模板,等T类型确定时,又会实例化出另一个不同的类。 - 当list存的是结构体类型Pos时,直接打印解引用迭代器后的值就会出现问题,因为解引用迭代器后拿到的是Pos类的对象,所以如果想要打印对象的值,我们可以重载Pos类的流插入运算符来实现,如果Pos类的成员变量是私有的,我们也可以用友元函数来解决。
- 如果是普通指针我们一般就用解引用,如果是结构指针,我们喜欢用→的访问方式。当迭代器指向的数据是一个结构体对象时,我们更偏向于用→来访问,当迭代器指向的数据是单个数据时,我们更偏向于用*来访问。
- 所以下面→解引用的使用方式更舒服一些,因为迭代器it所指向的内容是Pos结构体的对象。但it不是一个结构体指针,不能用→这样的访问方式,这该如何解决呢?因为it是一个对象,我们可以利用运算符重载来解决。
- 这里
template<class T, class Ref, class Ptr>
三个模板参数分别是提供 T& 、const T& 和提供解引用访问(→)
//迭代器 --- 自定义类型封装node*//分类:1.原生指针 2.用自定义类型对原生指针的封装,模拟指针的行为//即->迭代器: 要么就是原生指针,要么就是自定义类型对原生指针的封装,本质都是模拟指针的行为!template<class T, class Ref, class Ptr> //通过增加模板参数,提供多版本迭代器struct _list_iterator{typedef list_node<T> node; //加上模板参数才是类型,否则只是类名typedef _list_iterator<T, Ref, Ptr> self; //方便后面返回值,做参数node* _node; //指针_list_iterator(node* n) // 提供构造函数,遍历使用:_node(n){}Ref operator* () //提供const版本迭代器{return _node->_data;}//const 迭代器和普通迭代器的区别是什么? 一个内容可修改 ,一个内容不可被修改Ptr operator->() //->重载{return &_node->_data; //引用返回减少拷贝}//++返回什么? 返回迭代器呀!self& operator++() //遍历 前置++{_node = _node->_next;return *this;}self& operator++(int) //后置++{self tmp(*this); //拷贝构造一下_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const self& s){return _node != s._node;}bool operator ==(const self& s){return _node == s._node;}};
- 那这个迭代器是浅拷贝可以吗? 可以呀,因为这个是node的指针,浅拷贝后指针指向同一个node是可以的。那为什么没有报错呢? 因为我们没有提供析构函数。 而且不释放当然是正确的,因为这里结点是属于链表的,释放结点的工作应该交给链表类而不是迭代器类。
所以当内置类型涉及资源申请时,也并不一定就要写析构函数,还需要视情况而定。迭代器只是一个工具而已,释放也得由开辟空间的对象(list)来释放
10.对于迭代器的封装: 底层物理上都是指向这个对象的4个字节的指针。但语法的千差万别,主要还是运算符重载!
重点的两个接口 — insert 和 erase
- 实现insert时可以看到所传参数是迭代器,实际就是链表结点的地址,也是一个结构体指针,只不过我们对这个结构体指针进行了封装,这个迭代器就变成了一个对象,在实现上和数据结构初阶实现的带头双向循环链表没什么区别,仅仅是多了一个类的封装。
- 那insert这里迭代器会不会失效? 不会,pos指向节点没有发生地址改变
- erase这里迭代器失效吗 肯定失效。因为迭代器指向被干掉了。 所以我们可以加个断言啊---- 谁不可以erase? — 哨兵位头节点
- 迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
- 实现erase时返回删除位置的下一个位置的迭代器,以便于使用者更新erase之后的迭代器,防止产生迭代器失效问题。
//insert函数 pos前插入x//insert这里迭代器会不会失效? 不会,pos指向节点不会发生地址改变void insert(iterator pos, const T& x){node* cur = pos._node; //指针node* prev = cur->_prev;node* new_node = new node(x); //堆区申请prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;}//erase函数 删除pos位置void erase(iterator pos){assert(pos != end()); //防止删除哨兵节点node* next = pos._node->_next;node* prev = pos._node->_prev;prev->_next = next;next->_prev = prev;delete pos._node;}
小问题
- 在实现赋值重载函数时,为什么我们不能传引用?当然不能,因为这里是赋值,不是二者交换,所以应该传参调用其拷贝构造
//赋值重载list<T>& operator=(const list<T> l){swap(l);return *this;}
- list的反向迭代器:通过前面例子知道,反向迭代器的++就是正向迭代器的–,反向迭代器的–就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。
//反向迭代器// 适配器 -- 复用template<class Iterator, class Ref, class Ptr>struct Reverse_iterator{Iterator _it;typedef Reverse_iterator<Iterator, Ref, Ptr> Self;Reverse_iterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp = _it;return *(--tmp);}Ptr operator->(){return &(operator*());}Self& operator++(){--_it;return *this;}Self& operator--(){++_it;return *this;}bool operator!=(const Self& s){return _it != s._it;}};
- 显示报错报错 没有合适的默认构造函数怎么办? 提供缺省----缺省函数为什么用 T() :调用其默认构造
- 这个是很多人的疑惑:const对象在定义时有没有const属性? 答案是没有的!否则我们如何对数据进行初始化?所以这里是编译器的特殊处理罢了,站在语法的角度,就初始化后在赋予const属性
list模拟实现源码参考
list与vector的对比
vector
优点:
a.支持下标的随机访问
b.尾插尾删效率高
c.CPU高速缓存命中率高(局部性原理)
缺点:
a.前面部分插入删除数据效率低(插入和删除需要挪动数据,尤其是数据量大时效率较低)
b.扩容有消耗,还存在一定空间浪费(2倍扩容是比较合适的,开大了浪费空间,开小了需要频繁扩容)
list
优点:
a.按需申请释放空间,无需扩容
b.任意位置插入删除的时间复杂度是O(1),不考虑find查找位置,只是单纯erase和insert。
缺点:
a.不支持下标的随机访问
b.CPU高速缓存命中率低(局部性原理)
- vector的insert和erase之后迭代器都会失效,list在insert之后迭代器不会失效,erase之后迭代器会失效
- 在需要高效存储,支持随机访问,不关心插入删除效率,我们倾向于使用vector
- 大量插入和删除操作,不关心随机访问的情况,我们倾向于使用list,而且现实中项目中list用的蛮多的
少熬夜,早点休息!
相关文章:

【STL】list剖析及模拟实现
✍作者:阿润菜菜 📖专栏:C 初识list 1. list基本概况 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立…...

Go打包附件内容到执行文件
前言 如果我们的应用在启动的时候需要对数据库进行初始化(比如建表等), 可以通过读取.sql文件内容直接执行. 但是, 这样会带出一个问题: 在发送可执行文件的时候, 需要连带着附件文件, 并且相对路径还不能出错. 这样太麻烦了有时我们并不希望附件的内容被使用者看到 处于种种…...

Spring的配置属性
介绍 以前都是用XML来设置属性值,SpringBoot的自动配置可以简化Spring应用的开发。配置属性只是配置Spring应用上下文中bean的属性而已,可以通过多个源来配置,包括JVM系统属性、环境变量等。 Spring中有两种不同(但相关)的配置 bean装配&…...

132.《render-props, Hoc,自定义hooks 详解》
文章目录render-props 模式props 方式children 方式(推荐)Hoc(高阶组件)使用步骤示例props 丢失解决方案自定义 hook1.只执行一次2.防抖hook高阶组件与自定义hook有什么区别相同点不同点React 中代码逻辑复用有三种方式,render-props, Hoc&am…...

通过Session共享数据验证码进行用户登录
通过Session共享数据验证码进行用户登录 需求: 访问带有验证码的登录页面login.jsp。用户输入用户名,密码以及验证码。 ①。如果用户名和密码输入有误,跳转登陆页面,提示:用户名或密码错误。 ②。如果验证码输入有误…...

C++STL详解(六)——stack和queue
文章目录空间适配器stackstack的定义方式stack的使用stack的模拟实现queuequeue的定义方式queue的使用queue的模拟实现空间适配器 适配器是一种设计模式(设计模式是一套被反复使用的,多数人知晓的,经过分类编目的,代码设计经验的…...

javaEE 初阶 — CSS 的 基本语法 与 引入方式
文章目录1. 基本语法规范2. 三种引入方式1. 基本语法规范 CSS 的基本语法规范是由 选择器 和 若干个声明 组成的。 选择器选中一个元素之后,这些属性都是针对于这个元素展开的。 先来看一个没有 CSS 的效果。 <body><p>这是一个段落</p> </bo…...

QEMU启动ARM32 Linux内核
目录前言前置知识ARM Versatile Express开发板简介ARM处理器家族简介安装qemu-system-arm安装交叉编译工具交叉编译ARM32 Linux内核交叉编译ARM32 Busybox使用busybox制作initramfs使用QEMU启动ARM32 Linux内核模拟vexpress-a9开发板模拟vexpress-a15开发板参考前言 本文介绍采…...

than的用法合集
首先需要了解一下than的词性,其有两个词性,一个是介词,一个是连词。 介词后面一定要接上名词性的词语,比如 i am taller then him 我比我的老师高 连词就比较自由,一般用来连接两个句子;但是使用than连词词…...

Unet 基于TCGA颅脑肿瘤MRI分割(高阶API分割模型)
目录 1. 介绍 2. dice 指标 3. resnet34 作为 backbone 的分割 4. deeplabv3 图像分割 4.1 问题 4.2 训练 4.3 预测 5. MAnet 图像分割...

[NIPS 2017] Improved Training of Wasserstein GANs (WGAN-GP)
Contents IntroductionDifficulties with weight constraintsCapacity underuseExploding and vanishing gradientsGradient penaltyReferencesIntroduction WGAN 增加了 GAN 模型训练的稳定性,但有时仍然会有生成质量不高或难以收敛的问题。作者发现上述问题经常是由 WGAN 中…...

力扣-每天的领导和合伙人
大家好,我是空空star,本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目:1693. 每天的领导和合伙人二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.…...

考虑分配与合并,用GO实现GCMarkSweep
完整源码 ≧ω≦ 希望各位爸爸们,给我点赞吧 kokool/GCByGo: 《垃圾回收的算法与实现》有感而发 (github.com) 书接上文 我们之前不考虑分配与合并情况下,用GO实现GCMarkSweep(标记清除算法),而这次我们继续回顾书本…...

浙江大学海宁IMBA提面经验分享
先来介绍一下我的个人情况:本人毕业于浙江一所普通的本科院校,毕业已经6年了,在一家互联网公司担任市场部经理。其实在参加浙大IMBA项目提面之前,我也参加了浙大MBA项目的提面,可惜只拿到了良好的结果,所以…...

Mybatis源码分析系列之第四篇:Mybatis中代理设计模型源码详解
一: 前言 我们尝试在前几篇文章的内容中串联起来,防止各位不知所云。 1:背景 我们基于Mybatis作为后台Orm框架进行编码的时候,有两种方式。 //编码方式1 UserDao userDao sqlSession.getMapper(UserDao.class); userDao.quer…...

JDBC的API详解
🍎道阻且长,行则将至。🍓 目录 一、DriverManager 驱动管理类 1.注册驱动 2.获取数据库连接 二、Connection 数据库连接对象 1.获取执行对象 2.事务管理 三、Statement 1.执行DDL、DML语句 2.执行DQL语句 四、ResultSet 以JDBC快速…...

【深度强化学习】(4) Actor-Critic 模型解析,附Pytorch完整代码
大家好,今天和各位分享一下深度强化学习中的 Actor-Critic 演员评论家算法,Actor-Critic 算法是一种综合了策略迭代和价值迭代的集成算法。我将使用该模型结合 OpenAI 中的 Gym 环境完成一个小游戏,完整代码可以从我的 GitHub 中获得…...

SQL注入——文件上传
目录 一,mysql文件上传要点 二,文件上传指令 一句话木马 三,实例 1,判断注入方式 2,测试目标网站的闭合方式: 3,写入一句话木马 4,拿到控制权 一,mysql文件上传…...

【ESP32+freeRTOS学习笔记之“ESP32环境下使用freeRTOS的特性分析(新的开篇)”】
目录【ESP32freeRTOS学习笔记】系列新的开篇ESP-IDF对FreeRTOS的适配ESP-IDF环境中使用FreeRTOS的差异性简介关于FreeRTOS的配置关于ESP-IDF FreeRTOS Applications结语【ESP32freeRTOS学习笔记】系列新的开篇 ESP-IDF对FreeRTOS的适配 FreeRTOS是一个可以适用于多个不同MCU开…...

Uipath Excel 自动化系列18-RefreshPivotTable(刷新透视表)
活动描述 RefreshPivotTable(刷新透视表):如果透视表的数据源发生变化,需使用刷新透视表活动,该活动需与Use Excel File 活动选择的 Excel 文件一起使用。 使用如下图: RefreshPivotTable(刷新透视表)属性 属性 作用 Display…...

设计模式之不变模式
在并行软件开发过程中,同步操作是必不可少的。当多线程对同一个对象进行读写操作时,为了保证对象数据的一致性和正确性,有必要对对象进行同步操作,但同步操作对系统性能有损耗。不变模式可以去除这些同步操作,提高并行…...

C++11 map
C11中Map的使用Map是c的一个标准容器,她提供了很好一对一的关系,在一些程序中建立一个map可以起到事半功倍的效果,总结了一些map基本简单实用的操作!1. map最基本的构造函数;map<string , int >mapstring; map&l…...

docker基本命令 - 数据卷
作用 ● 做数据持久化。防止容器一旦停止运行,该容器中运行产生的数据就没了 ● 不同容器之间的数据共享(大鲸鱼背上各个小集装箱之间可以共享数据) 交互式命令使用 docker run -it -v / 宿主机的绝对路径目录:/容器内绝对路径目录 镜像名 docker run -it -v / 宿…...

SQL查漏补缺
有这么一道题,先看题目,表的内容如下 显示GDP比非洲任何国家都要高的国家名称(一些国家的GDP值可能为NULL)。 错误的查询: SELECT name FROM bbcWHERE gdp > ALL (SELECT gdp FROM bbc WHERE region Africa)正确的查询: SE…...

偏向锁撤销
偏向状态 一个对象创建时: 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的thread、epoch、age 都为 0。偏向锁是默认是延迟的,不会在程序启动时立…...

Qt版海康MV多相机的采集显示程序
创建对话框工程MultiCamera工程文件MultiCamera.pro#------------------------------------------------- # # Project created by QtCreator 2023-03-11T16:52:53 # #-------------------------------------------------QT core guigreaterThan(QT_MAJOR_VERSION, 4): …...

2023年江苏省职业院校技能大赛中职网络安全赛项试卷-教师组任务书
2023年江苏省职业院校技能大赛中职网络安全赛项试卷-教师组任务书 一、竞赛时间 9:00-12:00,12:00-15:00,15:00-17:00共计8小时。 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 基础设施设置与安全加固、网络安全事件响应、数…...

零基础小白如何自学网络安全成为顶尖黑客?
在成为黑客之前,你需要做两点准备: 1、学一门编程语言。学哪一门不重要,但你要参考一下下面的条例: C语言是Unix系统的基础。它(连同汇编语言)能让你学习对黑客非常重要的知识:内存的工作原理…...

外贸建站如何提高搜索引擎排名,吸引更多潜在客户?
在如今全球贸易日益繁荣的背景下,越来越多的企业开始重视外贸建站,并寻求提高搜索引擎排名以吸引更多潜在客户。 那么,如何才能有效地提高外贸网站的搜索引擎排名呢?本文将为您详细介绍几个有效的方法。 一、关键词优化 关键词…...

计算机网络考研-第一章学
计算机网学习总结第一章计算机系统概述1.1.1 导学1.1.2 操作系统的特征1.2 操作系统的发展与分类1.3 操作系统的运行环境1.3.1 操作系统的运行机制1.3.2 中断和异常1.3.3系统调用:1.3.4 操作系统的体系结构第一章总结第一章计算机系统概述 1.1.1 导学 1.1.2 操作系…...