封装哈希表

本文旨在讲解哈希表的封装,我们以哈希桶的结构来进行封装unorderedmap/set。要想实现封装哈希表,我们首先得先将哈希表的结构给搭建出来,然后再根据哈希桶的结构进一步封装unorderedmap/set!

下面我们先来实现哈希桶的结构,哈希桶的结构其实就是相同映射值的在一个桶内,然后把相同的桶内的元素通过指针链接起来即可,所以我们要先实现哈希表中的结点的结构体!因为其节点类似于链表的结构,所以里面存放的是节点的指针,以及该节点存储的值!

因为我们是用自己的方式来实现哈希表并进一步封装unorderedmap/set,所以我们得用自己的命名空间来包装!下面来看一下该哈希表结点的结构实现!步骤分为两大步:1.实现哈希表。

2.利用已有的哈希表来进行封装!

一、实现哈希表!

对于哈希表么,我们首先也得实现哈希表中的结构的结构体!上面已经讲过,该结构体类型类似于一种链表,所以我们现在就开始实现一下哈希表结点的结构体!

1.哈希表结点的结构

构造函数也是同样的,直接进行值的构造即可!

采用模版,首先一开始都将存储的元素即为pair类型,最后进行封装的时候再进行修改即可!


当哈希表的结点的结构体搭建出来的时候,我们就可以搭建我们得哈希表整体的结构了,对于哈希表而言,我们需要的成员是以哈希结点指针为元素的向量vector,以及有效元素的个数!

下面来看一下哈希表中的成员!

2.哈希表中的成员变量

都是基于模版给出的,可以适用于多种类型!

当表中的成员写完之后,我们首先要都该类进行构造函数和析构函数的编写!

3.构造函数

构造函数,就是完成初始化的动作,我们只需要对我们的vector向量开辟一段空间即可完成构造函数!

4.析构函数

因为成员变量中自定义的指针类型,所以系统默认的析构函数不能完成析构,需要我们手动释放,自己来实现析构函数!实现析构函数,只需要将表中存在的结点释放,然后置空即可!

对于析构函数,我们只需要遍历表,然后将表中存在的元素进行delete掉即可!然后一个桶内的元素清空之后,然后将表进行赋空即可!下面来看一下析构函数的实现!


构造函数以及析构函数实现之后,那么我们就开始实现一些相关操作的函数!

增删查这几个常见的函数!

5.Insert函数

对与Insert函数,我们首先需要关注的是是否需要扩容!当需要扩容的话,我们无需在新建节点了,只需要将结点的指针进行修改即可!我们需要定义一个新的vector,然后用于存储旧表中的元素!我们可以采用头插法来进行对新表的插入,最后将旧表中所有的元素插入完成之后,将旧表置空,然后交换新旧表即可!!因为新表的创建是在局部内,且在栈上进行创建的,所以不用担心有内存泄漏的问题!

下面来看一下Insert函数的实现!


实现了Insert函数之后,我们可以继续实现Find函数,如果找到Find的值时,返回该节点的指针即可,否则返回nullptr;我们要做的是首先要求出对应的映射值,然后在桶内进行遍历,若桶内存在与key值相同的元素,返回该节点的指针即可!下面我们来实现一下Find函数!

6.Find函数


实现了Find函数之后,我们再来实现Erase函数,需要注意的是,我们实现Erase函数的时候,不能复用Find函数来找到该节点,因为要删除一个节点的时候,我们需要其前驱节点以及后继节点才能正确删除,断开联系!需要注意的是,如果找到了要删除的节点,我们需要判断一下是否为头删,头删与中间节点的删除是不一样的!下面来看一下Erase函数的实现!

7.Erase函数!


HashFun的引进及实现!(用于将一个类型转化为整形!)

实现了上述函数之后,我们的哈希表基本大致实现!但是当我们插入字符类型的时候我们就会发现问题所在,因为字符类型无法自动转化为整形!这时就引进了仿函数HashFun,此时我们可以利用HashFun来将各种类型转化为整形!下面我们就来介绍一下如何实现HashFun,对于整形/浮点型等等我们直接进行强转成size_t即可,因为平常我们使用string的类型较多,所以我们对于string类型直接进行模版的特化,专门针对string类型写一个HashFun;HashFun的实现也是基于模版!下面来看一下HashFun的实现!

因为我们后期要一直使用这个仿函数,我们直接将其定义成全局即可!当引进了仿函数时,我们就需要对我们之前的代码进行修改,凡是设计到求余的操作,都要用到这个函数,将key转化为size_t类型!

所以我们直接在Hashtable该类中再加一个模版参数Hash 给他一个缺省参数!修改如下:

 再给一个缺省参数即可完成!下面我们还要声明一个这样的函数hf,下面代码凡是设计到求余的操作都要用到这个函数!那么我们就对Insert来进行改造!

其他函数凡是涉及取余都是同样的,这里不再修改其他了,就是将取余套一层hf函数即可;


二、unorderedmap/set封装

至此,我们的哈希桶结构已经完成,那么下一步就开始封装我们自己unorderdmap/set,对于我们自己的unorderedmap/set而言,我们只需要添加自己的命名空间,然后就可以与库中的unorderedmap/set进行区分!

因为基于泛型编程,我们对unorderedmap/set的封装总不能写两份代码的,所以我们要实现一种格式,让二者都能使用我们得哈希桶这个结构来进行对unorderedmap/set的封装。所以我们首先要进行修改的就是我们桶中的数据类型,我们只需要桶中的数据类型进行一种改变,即可使unorderedmap/set都能调用代码,同红黑树的封装一样,我们只需要将红黑树中的第二个模版参数当成数据节点的类型即可!下面是哈希桶中数据节点的类型的改变!以及哈希桶的模版参数的变化!

当我们使用map时,T就是一个pair的类型,当我们使用set的时候,T就是一个K的类型!那么我们应该如何求出我们T的类型,就再次引进了一个仿函数KeyofT。下面来看一下MapkofT/SetkofT的实现!

KeyofT的仿函数实现之后,我们的unorderedmap/set就可以使用一份代码来进行封装了,只不过我们需要在取K的时候套一层KeyofT函数即可!至此我们再将哈希表中的Insert...等函数封装到我们的unorderedmap/set即可完成这些函数的调用!

下面看一下我们的unorderd_map/set的实现。

1.Unorderd_Map

2.Unorderd_Set


至此,我们封装的UnorderedMap/Set也能正常使用!

三、迭代器的封装

下面我们再继续封装我们的迭代器!对于迭代器我们还是像以前那样用节点的指针来构造迭代器,但是当我们重载operator++的时候,我们发现仅仅有节点的指针是不行的,还需要有哈希表中的table向量,以及当前位置的映射值,这里我使用哈希表来给大家讲解一些关于类之间相互依赖的问题!所以我们的迭代器的构造就有节点的指针,哈希表,以及哈希映射值这三者组成! 

下面来看一下迭代器实现!

1.迭代器内的成员变量

 

2.迭代器的构造函数

需要注意的是:当我们对迭代器进行构造时,我们需要用到哈希表,而哈希表也需要用到我们的迭代器,这就造成了相互依赖的过程,这时我们只需要加一个前置声明即可解决我们的问题!即:在迭代器的前面加上对类模版哈希类的声明!需要注意的是:对类模版的前置声明时,也需要加上模版参数!代码如下:

 

3.重载operator++

迭代器既然已经实现了,那么我们就可以来重载operator++操作!在哈希表中++的操作,首先判断当前桶内是否还有下一节点,如果当前桶内没有节点,继续向下找其他桶,直到找到最后返回空即可!代码如下:

4.重载operator->

重载operator->即返回_node当前节点指向的值的地址!代码如下:

5.重载operator*

重载operator*即返回_node当前节点指向的值!

 

6.重载operator!=

下面我们再对表内进行迭代器的封装!

四、哈希表内对迭代器的封装!

我们只需要将迭代器在表内进行重定义即可!然后实现begin()和end()函数即可!代码如下:

需要注意的是:对内嵌类型进行typedef时,需要加上typename用于表示是一中类型!

 1.begin()函数的实现!

 2.end()函数的实现!


注意:因为迭代器中要访问到我们表中的私有成员变量,所以我们要进行友元类的声明!!对于模版友元的声明,我们需要将模版参数也加上去,才能正确进行声明!

此时,普通迭代器已经大致完成,要想实现在我们的unorderedmap/set内实现迭代器,只要再次进行封装即可!

3.Unordered_set对迭代器的封装!

再加上begin,end函数的实现,就可以完成范围for的使用了!

4.Unordered_Map对迭代器的封装!

Unordered_Map中的begin和end与上面Unordered_Set的同理!!


至此我们的迭代器也实现完成,那么我们发现当我们对值进行修改的时候,我们的值还能进行修改,但库中的却不能进行修改,所以我们应该再添加一个const迭代器,来进一步对我们的迭代器进行完善!


五、const迭代器的实现!

下面我们来实现一下const迭代器!

对于const迭代器,我们需要再次引进两个变量Ref,Ptr才能完成const迭代器的实现!

下面来看一下const迭代器的声明!!

然后将begin函数和const函数都添加一个const版本! 

1.Unordered_Set中的const迭代器!

然后再对set/map中进行封装即可!对于set而言我们只需将set中的普通迭代器和const迭代器都声明为const迭代器即可! 

但是对于set而言当我们仅仅将迭代器全部声明为const时,并不能解决问题,因为我们的this类型是const的类型,而在迭代器的构造中表是普通类型,当const变量给非const变量赋值时,就会导致权限的放大!那么我们只需要将迭代器的构造再写一个const版本试试!

此时构成了重载,但是当再次对旧表进行构造时,因为旧表也是普通对象,此时我们的表是const对象,所以也会导致权限的放大!那么我们再将表进行const声明即可! 这样就解决了set中key值能修改的问题!

2. Unordered_Map中的const迭代器!

对于Map类型,我们只需要对pair中的first不能修改,而second依旧不能修改!我们只需要对pair的first加上const修饰即可!

      

至此,我们的const迭代器也实现完成! 


下面我们就来实现一下operator[]函数!

六、operator[]函数的实现

对于实现operator函数我们的Insert/Find函数的返回值类型也要进行修改!

我们将Insert的返回值类型设置为pair<iterator,bool>类型,这时当我们在UnorderedSet中进行封装时,就会发现问题,迭代器转化的问题!对于红黑树那里,我们只需要将迭代器转化为Node*即可!但是对我们的哈希表中,迭代器有三部分组成,所以不能进行转化!那么我们只好用一个ret类型进行调用Insert的值,然后将ret对迭代器进行构造即可解决问题!

这里是因为我们知道迭代器的底层是什么,所以我们可以进行对pair的构造!! 

 下面就可以实现operato[],然后就可以实现统计次数!

 

至此UnorderedMap/Set已经实现!在进行封装的时候,一定要一步一步的来,不要想着一口气将所有的代码实现!否则很难完美的封装!

下面我把源代码发到下面,供大家参考!看完本文希望各位佬留下免费的关注和小心心!

        七、源码

1.Hashtable的实现!

#pragma once
//哈希表的实现!
//首先将结点的类型用结构体表示出来!
//然后哈希表主要是有这些结点的类型的向量vector/以及有效元素的个数组成!
#include<string>
#include<vector>
#include<iostream>
using namespace std;//区别于库中的哈希用自己的命名空间来进行实现1template<class K>
struct HashFun
{//仿函数的实现本质上就是对operator的重载 !size_t operator()(const K& key){return size_t(key);}
};//专门针对于string的一个模版特化!
template<>
struct HashFun<string>
{size_t operator()(const string& str){size_t ret = 0;for (auto &ch:str){ret *= 31;  //这时基于一种算法来实现的!×什么数都可以,就是为了减少冲突!ret += ch;}return ret;}
};
namespace Hashbucket
{//类似于链表结构  节点内存储值以及下一个节点的指针1//泛型编程,将节点的类型设置为一个T即可!template<class T>struct  Hashdata{T _data;Hashdata* _next;//构造函数!Hashdata(const T& data):_data(data),_next(nullptr){}};template<class K, class T, class KeyofT, class Hash = HashFun<K> >class Hashtable;//迭代器的构造!因为要使用到节点,所以放在节点的结构体下面进行声明!template<class K, class T,class Ref,class Ptr, class KeyofT, class Hash=HashFun<K>>struct __Iterator{typedef Hashdata<T> Node;typedef __Iterator<K, T, Ref,Ptr,KeyofT> Self;//迭代器的实现需要节点的指针,哈希表,以及哈希映射值,所以要声明这三个成员!size_t _hashi;Node* _node;const Hashtable<K, T, KeyofT>* _pht;//构造函数__Iterator(Node* node, Hashtable<K, T, KeyofT>* pht, size_t hashi):_hashi(hashi),_pht(pht),_node(node){}__Iterator(Node* node, const Hashtable<K, T, KeyofT>* pht, size_t hashi):_hashi(hashi), _pht(pht), _node(node){}Self& operator++(){//当前桶存在下一个节点!if (_node->_next){_node = _node->_next;return *this;}//当前桶后面没有节点了else{//寻找其他桶,找到不为空的桶返回即可!++_hashi;while (_hashi < _pht->_table.size()){Node* cur = _pht->_table[_hashi];if (cur){_node = cur;break;}++_hashi;}//最后判断_hashi的值,如果与表的大小一样,说明结束了!if (_hashi == _pht->_table.size()){_node = nullptr;}}return *this;}Ptr operator->()//返回当前节点对应的值的地址!{return &_node->_data;}//重载operator!=bool operator!=(const Self&s){return _node != s._node;}Ref operator*()//返回当前节点对应的值{return _node->_data;}};//搭建哈希表的框架!template<class K,class T,class KeyofT,class Hash>class Hashtable{public:template<class K, class T,class Ref,class Ptr, class KeyofT, class Hash >friend struct __Iterator;typedef Hashdata<T> Node;typedef typename __Iterator<K, T,T&,T* ,KeyofT> iterator;typedef typename __Iterator<K, T,const T&,const T*, KeyofT> const_iterator;iterator begin(){//从头遍历表,一旦发现有元素返回迭代器!for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return iterator(cur, this, i);}}return end();}iterator end(){//迭代器的最后 用空指针,哈希值用-1表示!return iterator(nullptr, this, -1); }const_iterator begin()const{//从头遍历表,一旦发现有元素返回迭代器!for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this, i);}}return end();}const_iterator end()const {//迭代器的最后 用空指针,哈希值用-1表示!return const_iterator(nullptr, this, -1);}Hash hf;KeyofT kot;//构造函数!Hashtable(){//对向量进行开辟一定的空间即可!_table.resize(10);}//析构函数!~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){iterator it = Find(kot(data));if (it != end()){return make_pair(it, false);}if (_n == _table.size()){//需要进行扩容!vector<Node*> newtable;size_t newsize = _table.size() * 2;   //默认每次扩容2倍!//开始遍历旧表,当旧表中存在元素的时候,对新表进行头插即可!//这里的头插只需要改变指针的指向,无需真正新建节点!for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];size_t hashi = hf(kot(data)) % _table.size();  //求出哈希映射值!while (cur){Node* next = cur->_next;  //提前记录下一个节点!因为下面需要修改cur->_next!//对新表进行头插!cur->_next = newtable[hashi];newtable[hashi] = cur;cur = next;}//将旧表中的桶置为空!!_table[hashi] = nullptr;}//最后交换两个向量即可!_table.swap(newtable);}else{//无需扩容  直接进行头插即可!//先求出hashi,即该节点的映射的位置!Node* newnode = new Node(data);   //堆中创建一个节点!以kv进行构造!size_t hashi = hf(kot(data)) % _table.size();//对当前位置进行头插即可!newnode->_next = _table[hashi];_table[hashi] = newnode;//有效元素++;++_n;return make_pair(iterator(newnode,this,hashi),true);}}iterator Find(const K& key){size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (key == kot(cur->_data)){//找到相等的值!直接返回cur即可!return iterator(cur,this,hashi);}cur = cur->_next;}return iterator(nullptr,this,-1);}bool Erase(const K& key){size_t hashi = hf(key) % _table.size();Node* pre = nullptr;Node* cur = _table[hashi];while (cur){if (cur->_kv.first == key){//找到要删除的节点了!//分两种情况 头删  正常删除1if (pre == nullptr){//头删!_table[hashi] = cur->_next;}else{pre->_next = cur->_next;}delete cur;return true;}pre = cur;cur = cur->_next;}return false;}private:vector<Node*> _table;  //存储结点指针的向量!size_t _n=0;  //有效元素的个数!默认给0!};//void test1()//{//	Hashtable<int, int> ht;//	ht.Insert(make_pair(4, 4));//	ht.Insert(make_pair(5, 5));//	ht.Insert(make_pair(7, 7));//	bool ret= ht.Erase(4);//	if (ret == true)//	{//		cout << "删除成功!";//	}//	std::cout << ret;//	int c = 0;//}//void test2()//{//	Hashtable<string, string> ht;//	ht.Insert(make_pair("string", "字符"));//	ht.Insert(make_pair("left", "左边"));//	//	int c = 0;//}
}

2.UnorderedMap

#pragma once#include"Hash.h"
namespace Zhj
{template<class K, class V, class Hash = HashFun<K>>class Unordered_Map{//创建仿函数struct MapkeyofT{//对于Map而言,K就是返回pair的first即可!const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename Hashbucket::Hashtable<K, pair< const K, V>, MapkeyofT>::iterator iterator;pair<iterator,bool > insert(const pair<K,V>&kv){return ht1.Insert(kv);}bool Erase(const K& key){return ht1.Erase(key);}iterator begin(){return ht1.begin();}iterator end(){return ht1.end();}/*	const_iterator begin(){return ht1.begin();}const_iterator end(){return ht1.end();}*/V& operator[](const K& key){pair<iterator, bool> ret = ht1.Insert(make_pair(key, V()));return ret.first->second;}const V& operator[](const K& key)const{pair<iterator, bool> ret = ht1.Insert(make_pair(key, V()));return ret.first->second;}Hashbucket::Hashtable<K, pair<const K,V>, MapkeyofT> ht1;};//void test2()//{//	Unordered_Map<string, string> m1;//	m1.insert(make_pair("string", "字符串"));//	m1.insert(make_pair("left", "左边"));//	for (auto& ch : m1)//	{//		ch.first += "x";//		ch.second += "x";//		cout << ch.first << ":" << ch.second << endl;//	}//	int c = 0;//	//}void test2(){string arr[] = { "西瓜", "西瓜","香蕉", "苹果", "苹果", "梨", "梨" };Unordered_Map<string, int> count_map;for (auto& e : arr){count_map[e]++;}for (auto& kv:count_map){cout << kv.first << ":" << kv.second << endl;}cout << endl;}}

3.UnorderedSet

#pragma once
#include"Hash.h"namespace Zhj
{template<class K,class Hash=HashFun<K>>class Unordered_Set{//创建仿函数struct SetkeyofT{//对于Set而言,K就是返回K即可!const K& operator()(const K& k){return k;}};public:Hashbucket::Hashtable<K, K, SetkeyofT> ht1;typedef typename Hashbucket::Hashtable<K, K, SetkeyofT>::const_iterator iterator;typedef typename Hashbucket::Hashtable<K, K, SetkeyofT>::const_iterator const_iterator;pair<const_iterator, bool> insert(const K& key){auto ret = ht1.Insert(key);return pair<const_iterator,bool>(const_iterator(ret.first._node, ret.first._pht, ret.first._hashi),ret.second);}//iterator begin()//{//	return ht1.begin();//}//iterator end()//{//	return ht1.end();//}const_iterator begin()const{return ht1.begin();}const_iterator end()const {return ht1.end();}};void test(){Unordered_Set<int> s;s.insert(9);s.insert(7);s.insert(6);s.insert(4);for (auto& ch : s){//ch += 50;cout << ch << " ";}int c = 0;cout << endl;}
}

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

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

相关文章

Internet Download Manager(IDM下载) v6.42.3 绿色版介绍

互联网下载管理器是一个广泛使用的软件&#xff0c;它可以帮助用户更好地管理和加速他们的下载。最新版本v6.42.3已经发布&#xff0c;它带来了一系列新功能和改进&#xff0c;让用户更加方便和快速地下载他们需要的文件。 新版本的互联网下载管理器增加了对最新浏览器的支持&…

1.Spring入门

1.1 Spring简介 Spring是一个轻量级Java 企业级应用程序开发框架&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架&#xff0c;为开发Java应用程序提供全面的基础架构支持。 Spring Fra…

Linux第80步_使用“信号量”实现“互斥访问”共享资源

1、创建MySemaphoreLED目录 输入“cd /home/zgq/linux/Linux_Drivers/回车” 切换到“/home/zgq/linux/Linux_Drivers/”目录 输入“mkdir MySemaphoreLED回车”&#xff0c;创建“MySemaphoreLED”目录 输入“ls回车”查看“/home/zgq/linux/Linux_Drivers/”目录下的文件…

Github: Github actions 自动化工作原理与多workflow创建

Github actions 1 &#xff09;概述 Github Actions 是Github官方推出的 CI/CD 解决方案 https://docs.githu.com/en/actions 优点 自动发布流程可减少发布过程中手动操作成本&#xff0c;大幅提升ci/cd效率&#xff0c;快速实现项目发布上线 缺点 存在较高的技术门槛需要利用…

Cloudways搭建WordPress外贸独立站完整教程

现在做个网站不比从前了&#xff0c;搭建网站非常的简单&#xff0c;主要是由于开源的CMS建站系统的崛起&#xff0c;就算不懂编程写代码的人也能搭建一个自己的网站&#xff0c;这些CMS系统提供了丰富的主题模板和插件&#xff0c;使用户可以通过简单的拖放和配置操作来建立自…

ZK vs FHE

1. 引言 近期ZAMA获得7300万美金的投资&#xff0c;使得FHE获得更多关注。FHE仍处于萌芽阶段&#xff0c;是未来隐私游戏规则的改变者。FHE需与ZK和MPC一起结合&#xff0c;以发挥最大效用。如&#xff1a; Threshold FHE&#xff1a;将FHE与MPC结合&#xff0c;实现信任最小…

第k个数——字典序

题目链接&#xff1a;1.第k个数 - 蓝桥云课 (lanqiao.cn) 样例解释&#xff1a; 输入13&#xff0c;得到的初始数组为1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#xff0c;7&#xff0c;8&#xff0c;9&#xff0c;10&#xff0c;11&#xff0c;12…

【微服务】分布式调度框架PowerJob使用详解

目录 一、前言 二、定时任务调度框架概述 2.1 为什么需要定时任务调度框架 2.2 定时任务调度使用场景 三、PowerJob 介绍 3.1 PowerJob 概述 3.2 PowerJob 功能特性 3.3 PowerJob 应用场景 3.4 PowerJob 与其他同类产品对比 四、PowerJob 部署 4.1 PowerJob 架构 4.…

综合知识篇06-软件架构设计考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例…

一命通关递归

递归 简介 递归是我们在学C语言的时候&#xff0c;就已经接触到了的一个概念&#xff0c;相信大家的递归都是从这里开始的&#xff1a; 但是&#xff0c;在老师念ppt的时候&#xff0c;伴随着一些前轱辘不转后轱辘转的语言&#xff0c;我们往往都没有太去了解递归的工作原理和…

车载测试面试:各大车企面试题汇总

本博主可协助大家成功进军车载测试行业 TBOX 深圳 涉及过T-BOX测试吗Ota升级涉及的台架环境是什么样的&#xff1f;上车实测之前有没有一个仿真环境台架环境都什么零部件T-BOX了解多少Linux和shell有接触吗 单片机uds诊断是在实车上座的吗 uds在实车上插的那口 诊断仪器是哪…

构造-析构-拷贝构造-赋值运算符重载-const成员函数

1. 类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么时候都不写时&#xff0c;编译器会自动生成以下6个成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器…

MacBook使用——彻底卸载并删除软件:NTFS for Mac

问题 之前因MacBook读写NTFS格式移动硬盘&#xff0c;我安装并使用了 Paragon NTFS for Mac &#xff0c;试用期结束后将其从【应用程序】中卸载移除了。但之后每次开机启动时&#xff0c;系统还是会弹出【激活】通知&#xff0c;如下图 解决 Step1、在用户目录下的 Library 目…

vue2语法-简略版

内容不全&#xff0c;发现看官方文档效果更好。 介绍 — Vue.js API — Vue.js 二、Vue指令 2.1 内容渲染指令 v-text&#xff0c;v-html 内容渲染指令用来辅助开发者渲染DOM元素的文本内容&#xff0c;常用的内容渲染指令有如下2个&#xff1a; v-text&#xff08;类似in…

项目进展(十一)--重新绘制ADS1285采集板并学习

声明&#xff1a;本人水平有限&#xff0c;博客可能存在部分错误的地方&#xff0c;请广大读者谅解并向本人反馈错误。   由于项目的需要&#xff0c;上周又设计了ADS1285的采集电路板&#xff0c;最近几天焊接了一下&#xff0c;重新进行测试。由于之前对ADC采集不是重点&am…

UDP数据报套接字编程

1.1UDP编程原理 对于UDP协议来说&#xff0c;具有无连接&#xff0c;面向数据报的特征&#xff0c;即每次都是没有建立连接&#xff0c;并且一次发送全部数据报&#xff0c;一次接收全部的数据报。Java中使用UDP协议通信&#xff0c;主要基于DatagramSocket类来发送或接收数据报…

C++:菱形继承与虚继承

看下面这个示例代码 class A{ public: int num10; A(){cout<<"A构造"<<endl;} virtual void fun(){cout<<"A虚函数"<<endl;} };class B:public A{ public: B(){cout<<"B构造"<<endl;} void fun(){cout<…

可视化图表:南丁格尔玫瑰图,来自历史上最著名的护士。

Hi&#xff0c;我是贝格前端工场的老司机&#xff0c;本文分享可视化图表设计的南丁格尔玫瑰图设计&#xff0c;欢迎老铁持续关注我们。 一、南丁格尔与玫瑰图 南丁格尔&#xff08;Florence Nightingale&#xff0c;1820年-1910年&#xff09;是一位英国护士和统计学家&…

按位操作符详解

大家好啊&#xff0c;我是情谊&#xff0c;今天我们来讨论一下按位操作符的知识点与应用&#xff0c;按位操作符有时候在解决一些问题的时候可以提供一个很好的解题思路&#xff0c;话不多说&#xff0c;我们直接来看&#xff01; 今天我们主要是从两个方面来讲述一下按位操作…

Redis:使用redis-dump导出、导入、还原数据实例

redis的备份和还原&#xff0c;借助了第三方的工具&#xff0c;redis-dump 1、安装必要环境 yum -y install zlib-devel openssl-devel2、安装redis-dump 安装ruby&#xff1a; ruby下载地址&#xff1a;https://www.ruby-lang.org/zh_cn/downloads/ 我下载的是 2.5.0 版本…