C++-封装unordered

本期我们来封装实现unordered系列,需要前置知识,没有看过哈希的建议先看看哈希,而且哈希的代码都在这里面,一会要用到

C++-哈希Hash-CSDN博客

目录

代码实现

迭代器

const迭代器

全部代码


代码实现

 首先我们要把V改为T,因为我们不知道他是k结构还是kv结构

//unordered_set.h
namespace bai {template<class K>class unordered_set{private:hash_bucket::HashTable<K, K> _ht;};
}
namespace bai {template<class K,class V>class unordered_map{private:hash_bucket::HashTable<K,pair<K,V>> _ht;};
}

然后我们把框架搭好

我们对照着看一下,如果是set,这里就是key,data就是key,如果是map,这里就是pair,data就是pair

接下来还要修改insert,find等等,这里就因为不知道是pair还是k的原因,所以我们需要再次来写KeyOfT

        struct SetKeyOfT{const K& operator()(const K& key){return key;}};struct MapKeyOfT{const K& operator()(const pair<K,V>& kv){return kv.first;}};

写完后别忘记加上 

bool Insert(const T& data){KeyOfT kot;if (Find(kot(data))){return false;}HashFunc hf;//负载因子到1时扩容if (_n == _table.size()){size_t newSize = _table.size() * 2;vector<Node*> newTable;newTable.resize(newSize, nullptr);//遍历旧表,把节点迁下来挂到新表for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;//头插到新表size_t hashi = hf(kot(cur->_data)) % newSize;cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();//头插Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return true;}Node* Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return cur;}cur = cur->_next;}return nullptr;}bool Erase(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){if (prev == nullptr){//头删_table[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}cur = cur->_next;}}

接着我们要修改insert,find和erase的代码,下面我们来看看修改了哪里,我们按顺序看

大概就是这些地方,具体的还要看上面的代码

我们简单测试一下,没有问题,下面我们来封装迭代器

迭代器

要实现迭代器,我们要想想,当前桶走完了,如何到下一个桶呢?也就是2号位的桶完了后如何到6号位

template<class T>struct HTiterator{typedef HashNode<T> Node;Node* _node;HTiterator(Node* node):_node(node){}operator++(){if (_node->_next){_node = _node->next;}else{}}};

我们先把框架写好,我们来看operator++,如果当前桶没有走完,就让他到下一个位置即可

如果我们知道当前位置是几号桶可以解决吗?也不行,因为我们还需要哈希表的对象,大家想一想,他的底层是一个指针数组,每一个指针又指向一个单链表,我们现在是需要在指针数组里移动

    template<class K, class T, class KeyOfT, class HashFunc>struct HTiterator{typedef HashNode<T> Node;typedef HTiterator<K, T, KeyOfT, HashFunc> Self;Node* _node;HashTable<K, T, KeyOfT, HashFunc>* _pht;HTiterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}Self& operator++(){if (_node->_next){//当前桶还没完_node = _node->_next;}else{KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();//从下一个位置开始查找下一个不为空的桶++hashi;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];return *this;}else{++hashi;}}_node = nullptr;}return *this;}};

所以我们需要把整个哈希表传过来,传一个pht的指针,而且这里和之前一样,不一定是可以直接取模,需要再套一层hashfunc,此时我们就可以计算出当前是第几个桶

接着我们开始找下一个不为空的桶,我们先对hashi++,从下一个位置开始寻找,如果当前桶不为空,我们就break,否则++hashi,循环结束后是有两种情况的,一种是可以找到,我们要让it指向下一个桶,另一种是我们已经找完了整个表,已经走到结尾了,我们让nullptr充当end的位置

另外这里可能会有人有疑问,为什么这里没有加typename? 只有不能区分静态变量和类型时才需要加

        T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}        bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}

我们顺手再把这几个也写一下

        typedef HTiterator<K, T, KeyOfT, HashFunc> iterator;iterator begin(){//找第一个桶for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return iterator(cur,this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}

接着我们在HashTable里加上begin和end,我们构造迭代器时第一个传cur,找不到传nullptr,第二个传this即可,this就是哈希表的指针

下面我们再套一层,在unordered里封装一层

注意,这里就需要加typename了,我们之前封装map和set时也是一样的

下面我们要开始解决各种问题了 

这里是一个相互依赖的问题

首先我们在哈希表里调用迭代器 ,迭代器在前,没有问题

但是我们在迭代器里又用了哈希表,这里谁在前谁在后都不行,不过还好这里用的是哈希表的指针,如果是对象就不好解决了

这里我们就要用到前置声明了,告诉编译器哈希表我们是有定义的,只不过可能在后面,先不要着急

 

此时问题就解决了 

我们调用一下迭代器,再次编译,出现了这样的错误,我们仔细看可以发现,这里是私有的问题

这里在类外边是不能访问私有的 ,所以这里可以写一个gettable,或者使用友元

是迭代器要访问哈希表,所以迭代器是哈希表的友元,类模板的友元需要带上模板参数 

此时我们的迭代器就可以跑起来了 

接下来我们需要解决可以修改的问题,也就是要实现const迭代器了

const迭代器

和之前一样,要实现const迭代器我们需要传T*和T&

于是我们修改迭代器的模板参数和self 

operator*和箭头的返回参数也要修改

友元声明也需要修改 

        const_iterator begin() const{//找第一个桶for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this);}}return const_iterator(nullptr, this);}const_iterator end() const{return const_iterator(nullptr, this);}

然后要在HashTable里加上begin和end

接着我们对外层的set封装,无论是iterator还是const_iterator,它们其实都是const_iterator,而且只提供了const版本的begin和end,和库里面是一样的,这里的begin和end的返回值无论是const_iterator还是iterator都是可以的,因为普通对象是可以调用const成员函数的

此时我们的第一个问题就解决了

接着我们编译,发现这里编译不通过,不仅仅是这里,上面也是编译不通过的,这里报错是无构造函数可以接受源类型

我们来看,这里的const修饰的谁?

修饰的是*this

所以这里this的类型应该是这样的

我们画出来就是这样的,这里权限放大,是传不过去的 ,我们之前写的const迭代器没有遇到这样的问题,因为以前的const迭代器是不需要传哈希表的,而只需要传一个节点的指针过去即可

我们来看这种方法,我们重载了一个构造函数,并且直接把_pht改为了const

 对于原来的构造函数,pht是const变为非const,是权限的缩小

那此时我们不要第一个构造,只要第二个可以吗?

也是可以的,普通迭代器传过来只有const,权限缩小

我们来看库里面是怎么解决的,首先是普通的迭代器 

这里有一个

 库里面写了两个迭代器,用两个迭代器类来实现

这里还都写成const了,相当于单独写了一个类来解决

我们的方法和库里面的方法,这两种方法都可以解决

map的话和以前一样,改为const K

 修改这些地方

 这样就解决了

下面我们来修改返回值

先修改一下find的返回值

然后是insert的 

然后我们把map和set封装的insert也改一下,然后就是set这里的问题了

这里看起来是这样的

但是我们看这里

 但是它们是不同区域的代码

 set不允许修改key,所以选择用const迭代器替代普通迭代器

所以这里的两个pair是不一样的,pair是一个类模板,实例化为两个不一样的类,是不一样的类型

也就是说上面的pair是这样的,所以会报错

map为什么没报错?

 因为map的iterator就是iterator

注意,set这里的问题和权限缩小之类无关,只有指针和引用才有权限缩小的概念,这里是类模板实例化不同的模板参数,导致它们是不同的类型

我们之前在封装map和set就讲过了,这是库里面list的实现办法,不太清楚的各位可以去我往期的map和set实现里再看一看,我把链接放在下面

map和set模拟实现-CSDN博客

下面这个圈主的函数,如果是普通迭代器,这里就是拷贝构造,是const迭代器时,是构造函数,是支持普通迭代器转换成const迭代器的构造,这是一个有双重意义的函数

我们来用这个举例子,我们想用aa1构造aa3

我们要加这样一个构造,但是这里报错了一个私有

原因是上面这里是A<T,Ref>

这里是A<T,T&>

当这里Ref也是T&时是同一个类,当Ref是const时就不是同一个类了

我们看为什么库里面不报错呢?

因为库里面是struct

我们去掉private就好了,这些都是细节,大家需要注意

现在回到我们的哈希表里

我们也加上这样一个构造

接着我们再次编译,发现了几个错误

首先是我们把这里改为it(这里之前是写错了)

然后是find返回时不能返回nullptr,这里返回end(),接着编译就可以通过了

我们上次在这里写的和这里是不一样的

这里我们是先接收pair,再用pair去调用它的构造

这样写是没问题的,这里相当于把两个参数提取了出来,单独调用pair构造,这里的ret.first是一个普通迭代器,在pair的初始化列表里,初始化列表对自定义类型会调用构造

而这里屏蔽的(我们之前写的),是编译器的不严格检查,如果检查的严格一点,是不通过的

首先这里调用pair的构造

我们上面的a<int>和a<const int>都不是同一个类型,这里也是一样的

所以我们把iterator取出来

 然后直接调用它的构造,就会走它的初始化列表,初始化列表对于自定义类型就会调用它的构造

虽然这里两种方法都可以编译通过,但是我们还是建议使用方法2,不然换一个编译器可能就不通过了 

此时我们的问题就解决了

我们上面那么多事情,都是因为它 

insert要返回pair,就引发了普通迭代器转换const迭代器的问题,为什么insert要返回pair呢?

因为我们要实现operator[ ] ,这里都是相关联的

        V& operator[](const K& key){pair<iterator,bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}

为什么我们这里接收时可以用iterator?

因为map的iterator就是iterator,和set不一样

此时我们就可以使用[ ] 了,和库里面一样

全部代码

#include"Hash.h"
//unordered_set.h
namespace bai {template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<const_iterator, bool> insert(const K& key){//return _ht.Insert(key);pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);return pair<const_iterator, bool>(ret.first, ret.second);}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}
#include"Hash.h"
//unordered_map.h
namespace bai {template<class K,class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<const K,V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<iterator,bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}V& operator[](const K& key){pair<iterator,bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}private:hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;};
}
#pragma once
#include<vector>
#include<iostream>
using namespace std;
//Hash.h
template<class K>
struct DefaultHashFunc
{size_t operator()(const K& key){return (size_t)key;}
};template<>
struct DefaultHashFunc<string>
{size_t operator()(const string& str){//BKDRsize_t hash = 0;for (auto ch : str){hash *= 131;hash += ch;}return hash;}
};namespace open_address {enum STATE //状态{EXIST,EMPTY,DELETE};template<class K, class V>struct HashData{pair<K, V> _kv;STATE _state = EMPTY;};//struct stringHashFunc//{//	size_t operator()(const string& str)//	{//		return str[0];//	}//};template<class K, class V, class HashFunc = DefaultHashFunc<K>>class HashTable{public:HashTable(){_table.resize(10);}bool Insert(const pair<K, V>& kv){if(Find(kv.first)){return false;}//扩容//if ((double)_n / _table.size() >= 0.7)if (_n * 10 / _table.size() >= 7){size_t newSize = _table.size() * 2;//重新映射HashTable<K, V, HashFunc> newHT;newHT._table.resize(newSize);for (size_t i = 0; i < _table.size(); i++){if (_table[i]._state == EXIST){newHT.Insert(_table[i]._kv);}}_table.swap(newHT._table);}//线性探测HashFunc hf;size_t hashi = hf(kv.first) % _table.size();while (_table[hashi]._state == EXIST){++hashi;hashi %= _table.size();//防止越界,这样可以回到数组开头}_table[hashi]._kv = kv;_table[hashi]._state = EXIST;++_n;return true;}HashData<const K, V>* Find(const K& key){//线性探测HashFunc hf;size_t hashi = hf(key) % _table.size();while (_table[hashi]._state != EMPTY){if (_table[hashi]._state == EXIST&& _table[hashi]._kv.first == key){return (HashData<const K, V>*) & _table[hashi];}++hashi;hashi %= _table.size();}return nullptr;}bool Erase(const K& key){HashData<const K, V>* ret = Find(key);if (ret){ret->_state = DELETE;--_n;return true;}return false;}private:vector<HashData<K, V>> _table;size_t _n; //存储有效数据的个数};
}namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};//前置声明template<class K, class T,class KeyOfT, class HashFunc>class HashTable;template<class K, class T, class Ptr, class Ref,class KeyOfT, class HashFunc>struct HTiterator{typedef HashNode<T> Node;typedef HTiterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;typedef HTiterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;Node* _node;const HashTable<K, T, KeyOfT, HashFunc>* _pht;/*HTiterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}*/HTiterator(Node* node,const HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node), _pht(pht){}//普通迭代器时是拷贝构造//cosnt迭代器时是构造HTiterator(const Iterator& it):_node(it._node), _pht(it._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->_next){//当前桶还没完_node = _node->_next;}else{KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();//从下一个位置开始查找下一个不为空的桶++hashi;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];return *this;}else{++hashi;}}_node = nullptr;}return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}};//set -> hash_bucket::HashTable<K, K> _ht;//map -> hash_bucket::HashTable<K, pair<K,V>> _ht;template<class K,class T,class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;//友元声明template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc >friend struct HTiterator;public:typedef HTiterator<K, T,T*,T&, KeyOfT, HashFunc> iterator;typedef	HTiterator<K, T,const T*,const T&, KeyOfT, HashFunc> const_iterator;iterator begin(){//找第一个桶for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return iterator(cur,this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{//找第一个桶for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this);}}return const_iterator(nullptr, this);}const_iterator end() const{return const_iterator(nullptr, this);}HashTable(){_table.resize(10,nullptr);}~HashTable(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}pair<iterator,bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if (it!=end()){return make_pair(it,false);}HashFunc hf;//负载因子到1时扩容if (_n == _table.size()){size_t newSize = _table.size() * 2;vector<Node*> newTable;newTable.resize(newSize, nullptr);//遍历旧表,把节点迁下来挂到新表for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;//头插到新表size_t hashi = hf(kot(cur->_data)) % newSize;cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();//头插Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return make_pair(iterator(newnode,this),true);}iterator Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur,this);}cur = cur->_next;}return end();}bool Erase(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){if (prev == nullptr){//头删_table[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}prev = cur;cur = cur->_next;}--_n;return false;}void Print(){for (size_t i = 0; i < _table.size(); i++){printf("[%d]->", i);Node* cur = _table[i];while (cur){cout << cur->_kv.first << ":" <<cur->_kv.second <<"->";cur = cur->_next;}printf("NULL\n");}cout << endl;}private:vector<Node*> _table; //指针数组size_t _n = 0; //有效数据};
}

以上即为本期全部内容,希望大家可以有所收获

如有错误,还请指正

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

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

相关文章

2023-10-06 LeetCode每日一题(买卖股票的最佳时机含手续费)

2023-10-06每日一题 一、题目编号 714. 买卖股票的最佳时机含手续费二、题目链接 点击跳转到题目位置 三、题目描述 给定一个整数数组 prices&#xff0c;其中 prices[i]表示第 i 天的股票价格 &#xff1b;整数 fee 代表了交易股票的手续费用。 你可以无限次地完成交易&…

STM32复习笔记(一):软件配置工程创建

目录 Preface&#xff1a; Hardware-Configuration & Software-Environment&#xff1a; &#xff08;一&#xff09;新建项目工程 &#xff08;二&#xff09;工程配置 &#xff08;三&#xff09;配置外设 &#xff08;四&#xff09;项目管理 &#xff08;五&…

区别对比表:阿里云轻量服务器和云服务器ECS对照表

阿里云轻量应用服务器和云服务器ECS区别对照表&#xff0c;一看就懂的适用人群、使用场景、优缺点、使用限制、计费方式、网路和镜像系统全方位对比&#xff0c;阿里云服务器网分享ECS和轻量应用服务器区别对照表&#xff1a; 目录 轻量应用服务器和云服务器ECS区别对照表 轻…

51单片机音乐闹钟秒表倒计时整点报时多功能电子钟万年历数码管显示( proteus仿真+程序+原理图+报告+讲解视频)

51单片机音乐闹钟秒表倒计时整点报时多功能电子钟万年历数码管显示( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 程序代码4.原理图5. 设计报告6. 设计资料内容清单 51单片机音乐闹钟秒表倒计时整点报时多功能电子钟万年历数码管显…

MySQL视图、用户管理

目录 视图概念和操作 视图基本操作 视图规则和限制 用户管理 用户 视图概念和操作 什么是视图&#xff1f; 视图是一个虚拟表&#xff0c;由一个或多个基本表的查询结果组成&#xff08;视图是存储在数据库中的查询的SQL 语句&#xff0c;不在数据库中以存储的数据值集形式…

Verilator: sh: 1: exec: < PATHNAME >: not found

事出有因 I assumed that I install verilator via git clone its source, so I need to set the export VERILATOR_ROOT. At first, due to laziness, I just copy the code directly to my ubuntu. so stupid I am!!! Then, I recoginzed that the pathname is not real, …

wireshark of tshark tools v3.4.0版本 支持json

tshark(1) Install tshark (Wireshark) Ver.3.4.0 on CentOS7 --It must be "ps", "text", "pdml", "psml" or "fields". TCP 协议中的三次握手和四次挥手是 TCP 连接建立和关闭的过程。 三次握手 客户端向服务器发送 SYN…

JMeter学习第一、二、三天

首先&#xff0c;我们来了解一下到底什么是接口测试与性能测试&#xff1a; 接口测试 定义 接口测试主要关注系统组件之间的交互&#xff0c;确保各个接口按预期工作。这包括验证传递的数据、数据格式、调用的频率和其他与接口调用相关的任何限制。 目的 确保系统的各个组件可…

mac清理垃圾的软件有哪些?这三款我最推荐

没错&#xff0c;Mac电脑真的好用&#xff0c;但是清理系统垃圾可不是件容易的事。由于Mac系统的封闭性&#xff0c;系统的缓存垃圾常常隐藏得让人发现不了。不过&#xff0c;别担心&#xff01;有一些专业的Mac清理软件可以帮你解决这一系列问题&#xff0c;让清理垃圾变得轻松…

10链表-单链表构造LinkedList

目录 LeetCode之路——707. 设计链表 分析&#xff1a; Code&#xff1a; LeetCode之路——707. 设计链表 你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;n…

什么是TF-A项目的长期支持?

安全之安全(security)博客目录导读 问题&#xff1a;Trusted Firmware-A社区每六个月发布一次代码。然而&#xff0c;对于生产中的平台&#xff0c;该策略在维护、重要软件修复的向后兼容性、获得最新的安全缓解措施和整体产品生命周期管理方面不具备可扩展性。 开源软件项目&…

【C++设计模式之原型模式:创建型】分析及示例

简介 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它允许通过复制已有对象来生成新的对象&#xff0c;而无需再次使用构造函数。 描述 原型模式通过复制现有对象来创建新的对象&#xff0c;而无需显式地调用构造函数或暴露对象的创建…

LDGRB-01 用于在边缘处理人工智能的嵌入式硬件

LDGRB-01 用于在边缘处理人工智能的嵌入式硬件商业和企业中的IT系统正在全面快速发展&#xff0c;一个不断增长的趋势正在将计算能力推向边缘。Gartner预测&#xff0c;到2025年&#xff0c;边缘计算将处理75%的数据由所有用例产生&#xff0c;包括工厂、医疗保健和运输中的用…

[GXYCTF2019]禁止套娃 无回显 RCE 过滤__FILE__ dirname等

扫除git 通过githack 获取index.php <?php include "flag.php"; echo "flag在哪里呢&#xff1f;<br>"; if(isset($_GET[exp])){if (!preg_match(/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i, $_GET[exp])) {if(; preg_replace(/[a-z,_]\(…

Vue中如何进行响应式图像与图片懒加载优化

Vue中响应式图像与图片懒加载优化 在现代的Web开发中&#xff0c;图像在网站性能和用户体验方面扮演着至关重要的角色。然而&#xff0c;加载大量的图像可能会导致网页加载速度变慢&#xff0c;从而影响用户的满意度。为了解决这个问题&#xff0c;Vue.js提供了一些强大的工具…

【gitlab】git push -u origin master 报403

问题描述 gitlab版本&#xff1a;14.0.5 虚拟机版本&#xff1a;centos7 项目&#xff1a;renren-fast 原因分析 .git -> config目录下 url配错 但这个url不是手动配置的&#xff0c;还不知道怎么生成。 解决方法 把配置错误的url改成gitlab的project的url 这样&#…

Android学习之路(20) 进程间通信

IPC IPC为 (Inter-Process Communication) 缩写&#xff0c;称为进程间通信或跨进程通信&#xff0c;指两个进程间进行数据交换的过程。安卓中主要采用 Binder 进行进程间通信&#xff0c;当然也支持其他 IPC 方式&#xff0c;如&#xff1a;管道&#xff0c;Socket&#xff0…

VS Code更改软件的语言

刚刚安装好的 vscode 默认是英文&#xff0c;可以安装中文扩展包。如图&#xff1a; 重启即可更换为中文。 如果想切换为英文&#xff0c;可以 Ctrl Shift P&#xff0c;打开命令面板。 输入 Configure DIsplay Language&#xff0c;如图&#xff1a; 可以在中英文之间切换…

10.04

QT实现TCP服务器客户端搭建的代码&#xff0c;现象 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpSocket> #include <QMessageBox>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : p…

网络安全面试题汇总(附答案)

作为从业多年的网络安全工程师&#xff0c;我深知在面试过程中面试官所关注的重点及考察的技能点。网络安全作为当前信息技术领域中非常重要的一部分&#xff0c;对于每一个从事网络安全工作的人员来说&#xff0c;不仅需要掌握一定的技术能力&#xff0c;更需要具备全面的综合…