MFC中消息映射与命令传递机制初探
本文主要根据侯捷《深入浅出MFC》整理而成,主要讲述MFC消息映射与传递机制。
一 如何形成消息映射网
1 在源文件加入实现消息映射表格代码
首先你必须在头文件中(.H)声明消息映射表格
class CScribbleDoc : public CDocument
{...DECLARE_MESSAGE_MAP()
};
然后在实现文件中(.CPP)实现此表格:
BEGIN_MESSAGE_MAP(CScribbleDoc, CDocument)//{{AFX_MSG_MAP(CScribbleDoc)ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll)ON_COMMAND(ID_PEN_THICK_OR_THIN, OnPenThickOrThin)...//}}AFX_MSG_MAP
END_MESSAGE_MAP()
这其中出现三个宏。第一个宏BEGIN_MESSAGE_MAP 有两个参数,分别是拥有此消息映射表之类,及其父类。第二个宏是ON_COMMAND,指定消息处理函数名称。第三个宏END_MESSAGE_MAP 作为结尾记号。至于夹在BEGIN_ 和END_ 之中奇奇怪怪的说明符号//}} 和//{{,是ClassWizard 产生的,也是用来给它自己看的。
2 消息映射表格的构建
消息映射的本质其实是一个巨大的数据结构,用来为诸如WM_PAINT 这样的标准消息决定流动路线,使它得以流到父类去;也用来为WM_COMMAND 这个特殊消息决定流动路线,使它能够七拐八弯地流到类继承结构的旁支去。我们看看在头文件和源文件中这些宏的定义是什么。
首先,头文件中的DECLARE_MESSAGE_MAP宏定义为:
#define DECLARE_MESSAGE_MAP() \
protected: \static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \virtual const AFX_MSGMAP* GetMessageMap() const; \
然后,在实现文件中的宏定义为:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \const AFX_MSGMAP* theClass::GetMessageMap() const \{ return GetThisMessageMap(); } \const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \{ \typedef theClass ThisClass; \typedef baseClass TheBaseClass; \static const AFX_MSGMAP_ENTRY _messageEntries[] = \{#define END_MESSAGE_MAP() \{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \}; \static const AFX_MSGMAP messageMap = \{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] };\return &messageMap; \}\
其中AFX_MSGMAP结构表示本类和基类的消息映射表地址信息,定义如下:
struct AFX_MSGMAP
{const AFX_MSGMAP* pBaseMap; //基类的消息处理映射表地址const AFX_MSGMAP_ENTRY* lpEntries; //本类的消息映射表地址,是个数组
};
AFX_MSGMAP_ENTRY(代表消息映射表的每个条目)是这样的形式
struct AFX_MSGMAP_ENTRY
{UINT nMessage; // windows messageUINT nCode; // control code or WM_NOTIFY codeUINT nID; // control ID (or 0 for windows messages)UINT nLastID; // used for entries specifying a range of control id'sUINT_PTR nSig; // signature type (action) or pointer to message #AFX_PMSG pfn; // routine to call (or special value)
};/*
AFX_MSGMAP_ENTRY包括
1. Windows消息号
2. 通知消息代码(notification code,对消息的更多描述,例如EN_CHANGED或CBN_DROPDIOWN等)
3. 控件ID(命令消息为0)
4. 一个签名记号
5. CCmdTarget派生类的成员函数,也就是消息对应的响应函数。
*/
任何一个ON_宏会把这六个项目初始化起来。例如:
#define ON_COMMAND(id, memberFxn) \{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },
#define ON_WM_CREATE() \{ WM_CREATE, 0, 0, 0, AfxSig_is, \(AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate },
#define ON_WM_DESTROY() \{ WM_DESTROY, 0, 0, 0, AfxSig_vv, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnDestroy },
#define ON_WM_MOVE() \{ WM_MOVE, 0, 0, 0, AfxSig_vvii, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(int, int))OnMove },
#define ON_WM_SIZE() \{ WM_SIZE, 0, 0, 0, AfxSig_vwii, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, int, int))OnSize },
#define ON_WM_ACTIVATE() \{ WM_ACTIVATE, 0, 0, 0, AfxSig_vwWb, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CWnd*,
BOOL))OnActivate },
#define ON_WM_SETFOCUS() \{ WM_SETFOCUS, 0, 0, 0, AfxSig_vW, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(CWnd*))OnSetFocus },
#define ON_WM_PAINT() \{ WM_PAINT, 0, 0, 0, AfxSig_vv, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnPaint },
#define ON_WM_CLOSE() \{ WM_CLOSE, 0, 0, 0, AfxSig_vv, \(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnClose },
.../*
可以看到,最后一个函数指针进行了三次类型转换,第一次static_cast操作符可以检查函数类型是
否符合要求,第二次(AFX_PMSGW)和第三次(AFX_PMSG) 是为了类型安全,具体说来,如果一个继承
体系中包含多重继承的话,可能在子类强转为祖父类时出现二义性(例如,子类D从两个父类B、C继
承而来,而父类B和C都包含一个祖父类A,此时,将子类D转为A时,编译器会提示二义性错误)。总
之,需要把相应的消息处理函数转为统一的AFX_PMSG形式。
*/
从以上宏的定义可以看出,在定义消息映射表的时候,实际上在类中定义了一个静态的数组,这个数组中每一项是AFX_MSGMAP_ENTRY类型的数据,而AFX_MSGMAP_ENTRY包含了不同消息的消息号、ID值、通知消息代码、签名记号以及消息处理函数;
另外,通过GetMessageMap方法,可以获取AFX_MSGMAP结构,而这个结构体可以获取本类以及基类的消息映射表地址。
至此,我们的消息映射网初步形成,可以想见,我们新建每一个窗口类,都可以有自己的消息映射表格,也可以找到基类的消息映射表格,如此一直上溯,肯定可以找到默认消息处理函数。
二 消息的传递与响应
我们的消息映射网已经搭建好,那么窗口如何根据消息选择相应的处理函数呢?
首先要说,窗口接收到消息后,操作系统通过回调函数,最终调用的是窗口的CWnd::OnWndMsg函数。该函数实现如下:
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{LRESULT lResult = 0;union MessageMapFunctions mmf;mmf.pfn = 0;CInternalGlobalLock winMsgLock;// Windows消息单独处理if (message == WM_COMMAND){if (OnCommand(wParam, lParam)){lResult = 1;goto LReturnTrue;}return FALSE;}........// 通告消息单独处理if (message == WM_NOTIFY){NMHDR* pNMHDR = (NMHDR*)lParam;if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))goto LReturnTrue;return FALSE;}........//下面这些就是标准的Windows消息const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); //找到最底层子类的消息映射表UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);winMsgLock.Lock(CRIT_WINMSGCACHE);AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];const AFX_MSGMAP_ENTRY* lpEntry;if (........) //检查是否在cache之中{........}else{pMsgCache->nMsg = message;pMsgCache->pMessageMap = pMessageMap;//以下循环请仔细查看,其代码逻辑就是直线上溯,一层一层的找详细映射表,//直到找到为止。for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;pMessageMap = (*pMessageMap->pfnGetBaseMap)()){if (message < 0xC000){// 开始查找符合条件的消息if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0)) != NULL){pMsgCache->lpEntry = lpEntry;winMsgLock.Unlock();goto LDispatch;}}else{// registered windows messagelpEntry = pMessageMap->lpEntries;while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL){UINT* pnID = (UINT*)(lpEntry->nSig);ASSERT(*pnID >= 0xC000 || *pnID == 0);// must be successfully registeredif (*pnID == message){pMsgCache->lpEntry = lpEntry;winMsgLock.Unlock();goto LDispatchRegistered;}lpEntry++; // keep looking past this one}}}pMsgCache->lpEntry = NULL;winMsgLock.Unlock();return FALSE;}LDispatch:mmf.pfn = lpEntry->pfn; //这个神秘的联合体待会详解,你只需知道这个变量代表一个函数指针switch (lpEntry->nSig) //通过AFX_MSGMAP_ENTRY条目中的nSig变量,确定真正的函数形式{default:ASSERT(FALSE);break;case AfxSig_l_p:{CPoint point(lParam);lResult = (this->*mmf.pfn_l_p)(point);break;}case AfxSig_b_D_v:lResult = (this->*mmf.pfn_b_D)(CDC::FromHandle(reinterpret_cast<HDC>(wParam)));break;case AfxSig_l_D_u:lResult = (this->*mmf.pfn_l_D_u)(CDC::FromHandle(reinterpret_cast<HDC>(wParam)), (UINT)lParam);break;case AfxSig_b_b_v:lResult = (this->*mmf.pfn_b_b)(static_cast<BOOL>(wParam));break;case AfxSig_b_u_v:lResult = (this->*mmf.pfn_b_u)(static_cast<UINT>(wParam));break;case AfxSig_b_h_v:lResult = (this->*mmf.pfn_b_h)(reinterpret_cast<HANDLE>(wParam));break;... ... }goto LReturnTrue;LDispatchRegistered: // for registered windows messagesASSERT(message >= 0xC000);ASSERT(sizeof(mmf) == sizeof(mmf.pfn));mmf.pfn = lpEntry->pfn;lResult = (this->*mmf.pfn_l_w_l)(wParam, lParam);LReturnTrue:if (pResult != NULL)*pResult = lResult;return TRUE;
}const AFX_MSGMAP_ENTRY* AFXAPI
AfxFindMessageEntry(const AFX_MSGMAP_ENTRY* lpEntry,UINT nMsg, UINT nCode, UINT nID)
{... ... //以汇编语言处理,加快速度// C version of search routinewhile (lpEntry->nSig != AfxSig_end){if (lpEntry->nMessage == nMsg && lpEntry->nCode == nCode &&nID >= lpEntry->nID && nID <= lpEntry->nLastID){return lpEntry;}lpEntry++;}return NULL; // not found
}
通过该函数可以看出,如果是Windows消息的话,采取的是直线上溯的方式,一一比较消息和消息映射表中的条目是否相符。查找函数的实现函数为AfxFindMessageEntry,这个函数其实就是迭代本类中的消息映射表,尝试找到nMessage、nCode、nID都相同的条目。如果在这个子类中没有找到符合的条目,就到这个上一层父类中去找,直到找到为止。
那么,找到会怎么做?怎么调用相关的响应函数?这里的关键是AFX_MSGMAP_ENTRY条目中的nSig变量。
nSig变量可能的值如下:
enum AfxSig
{AfxSig_end = 0, // [marks end of message map]AfxSig_bD, // BOOL (CDC*)AfxSig_bb, // BOOL (BOOL)AfxSig_bWww, // BOOL (CWnd*, UINT, UINT)AfxSig_hDWw, // HBRUSH (CDC*, CWnd*, UINT)AfxSig_hDw, // HBRUSH (CDC*, UINT)AfxSig_iwWw, // int (UINT, CWnd*, UINT)AfxSig_iww, // int (UINT, UINT)AfxSig_iWww, // int (CWnd*, UINT, UINT)AfxSig_is, // int (LPTSTR)AfxSig_lwl, // LRESULT (WPARAM, LPARAM)AfxSig_lwwM, // LRESULT (UINT, UINT, CMenu*)AfxSig_vv, // void (void)AfxSig_vw, // void (UINT)AfxSig_vww, // void (UINT, UINT)AfxSig_vvii, // void (int, int) // wParam is ignoredAfxSig_vwww, // void (UINT, UINT, UINT)AfxSig_vwii, // void (UINT, int, int)AfxSig_vwl, // void (UINT, LPARAM)AfxSig_vbWW, // void (BOOL, CWnd*, CWnd*)
... ...
};
AfxSig枚举类型最后几位代表函数类型,例如 AfxSig_bWww代表函数返回值为bool型,函数参数依次为CWnd*, UINT和 UINT。
程序通过查看AFX_MSGMAP_ENTRY条目中的nSig变量值,就可以知道条目中消息响应函数的函数类型。刚刚我们说过了,消息响应函数表中所存储的都是AFX_PMSG 类型的函数指针,怎么转为正确的函数指针呢?
MFC将所有可能的响应函数形式定义为了一个联合体变量。
union MessageMapFunctions
{AFX_PMSG pfn; // generic member function pointer// specific type safe variantsBOOL (AFX_MSG_CALL CWnd::*pfn_bD)(CDC*);BOOL (AFX_MSG_CALL CWnd::*pfn_bb)(BOOL);BOOL (AFX_MSG_CALL CWnd::*pfn_bWww)(CWnd*, UINT, UINT);BOOL (AFX_MSG_CALL CWnd::*pfn_bHELPINFO)(HELPINFO*);HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDWw)(CDC*, CWnd*, UINT);HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDw)(CDC*, UINT);int (AFX_MSG_CALL CWnd::*pfn_iwWw)(UINT, CWnd*, UINT);int (AFX_MSG_CALL CWnd::*pfn_iww)(UINT, UINT);int (AFX_MSG_CALL CWnd::*pfn_iWww)(CWnd*, UINT, UINT);int (AFX_MSG_CALL CWnd::*pfn_is)(LPTSTR);LRESULT (AFX_MSG_CALL CWnd::*pfn_lwl)(WPARAM, LPARAM);LRESULT (AFX_MSG_CALL CWnd::*pfn_lwwM)(UINT, UINT, CMenu*);void (AFX_MSG_CALL CWnd::*pfn_vv)(void);void (AFX_MSG_CALL CWnd::*pfn_vw)(UINT);void (AFX_MSG_CALL CWnd::*pfn_vww)(UINT, UINT);void (AFX_MSG_CALL CWnd::*pfn_vvii)(int, int);void (AFX_MSG_CALL CWnd::*pfn_vwww)(UINT, UINT, UINT);void (AFX_MSG_CALL CWnd::*pfn_vwii)(UINT, int, int);... ...
};
然后就非常容易了,如果说在消息映射表中知道了相符的条目,就可以知道消息对应的消息响应函数的地址什么(就是函数的名称)。然后,根据AFX_MSGMAP_ENTRY条目中的nSig变量值,选择联合体中合适的成员就好了。以下是一个例子。
static BOOL DispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo)// return TRUE to stop routing
{union MessageMapFunctions mmf; //定义一个联合体变量,这个变量实际是某个函数指针mmf.pfn = pfn;BOOL bResult = TRUE; // default is ok... ...//以下根据AFX_MSGMAP_ENTRY条目中的nSig变量值,调用函数正确的形式和参数。//注意:函数的参数实际最初是由消息体中的wParam和lParam传递进来的switch (nSig){default: // illegalASSERT(FALSE);return 0;break;case AfxSigCmd_v:// normal command or control notificationASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKEDASSERT(pExtra == NULL);(pTarget->*mmf.pfnCmd_v_v)();break;case AfxSigCmd_b:// normal command or control notificationASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKEDASSERT(pExtra == NULL);bResult = (pTarget->*mmf.pfnCmd_b_v)();break;case AfxSigCmd_RANGE:// normal command or control notification in a rangeASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKEDASSERT(pExtra == NULL);(pTarget->*mmf.pfnCmd_v_u)(nID);break;case AfxSigCmd_EX:// extended command (passed ID, returns bContinue)ASSERT(pExtra == NULL);bResult = (pTarget->*mmf.pfnCmd_b_u)(nID);break;... ...
}
这是MFC动人的一幕,不同的消息,参数不一样,返回值也不一样,而且在定义的时候只是一个指针,可是在调用的时候却有各种各样的方式。用了一个union变量,就将所有不同形态函数统一为一个,这太牛了。
可是为什么要这样做呢?为了降低程序的空间复杂度。如果我们使用C++常用的虚函数来实现多态,需要在类中维护一个虚函数表,从而实现基类指针调用子类方法的效果。由于Windows消息众多,如果在每个类中维护一个虚函数表,大大增加了程序了空间复杂度。
三 最后一个问题,成员解除引用(->*)运算符的用法
还剩下最后一个问题,比如上面函数中的这条语句
case AfxSigCmd_v:(pTarget->*mmf.pfnCmd_v_v)();break;
pTarget是一个CCmdTarge指针,mmf.pfnCmd_v_v指针类型如下:
void (AFX_MSG_CALL CCmdTarget::*pfnCmd_v_v)();
可以看出,这分别是一个基类指针,调用了一个基类方法,怎么就最终调用成子类的方法了呢?这里关键是要理解成员解除引用(->*)运算符。基类指针使用成员解除引用运算符时,基类指针(实际指向子类对象)把this指针压栈,然后程序直接转到这个子类成员函数的地址,因此也就找到了子类相应的类方法。
以下代码模式了这个过程。
#include<iostream>
using namespace std;class CBase
{
public:void BasePrintMsg() {cout << "In Base class" << endl;}
};class CDerive :public CBase
{
public:CDerive() :m_iDerive(3) {}int GetInt(int m) {return m_iDerive * m;}double GetDouble(int m, double d) {return m * d;}
private:int m_iDerive;
};typedef void (CBase::* pBaseVoidFun)();union UMapFuns
{//请注意,以下三个成员都是基类的成员函数的函数指针pBaseVoidFun pfn;double (CBase::*pfn_double)(int m, double d);int (CBase::*pfn_int)(int m);
};int main()
{UMapFuns uMapFun; //向MFC学习,定义一个联合体变量,表示函数指针uMapFun.pfn = (pBaseVoidFun)&CDerive::GetInt; //子类方法转为基类方法赋给联合体CDerive cDeriveObj;CBase* pBase = &cDeriveObj;//调用成功,关键要理解uMapFun.pfn_int的地址实际上是子类成员函数的地址,子类对象//调用解除引用运算符时,不过是将子类对象的地址压栈,让子类成员函数能够根据这个//地址找到类的其他成员。int i = (cDeriveObj.*(uMapFun.pfn_int))(3); //调用成功,用基类指针调用子类函数。关键要理解uMapFun.pfn_int的地址实际上是子类//成员函数的地址,基类指针指向子类对象的地址,该地址压栈后,//子类成员函数根据这个地址找到的类的数据成员都是正确的。int i2 = (pBase->*(uMapFun.pfn_int))(4); uMapFun.pfn = (pBaseVoidFun)&CDerive::GetDouble;double d = (pBase->*(uMapFun.pfn_double))(3, 4.0); //调用成功,用基类指针调用子类函数return 0;
}
That is all!
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- leetcode-43. 字符串相乘
题目 此题强烈建议查看参考资料学习 代码 /*** param {string} num1* param {string} num2* return {string}*/ // 模拟乘法 var multiply function(num1, num2) {let len1 num1.length;let len2 num2.length;let res new Array(len1 len2).fill(0);// 双重遍历模拟乘法f…...
2024/4/8 19:36:36 - 自学Java 运算符
一.运算符 1.位运算:位运算是直接对整数的二进制的运算 结论: 1.位运算符操作的都是整形的数据 2.①<< : 在一定范围内,每向左移1位,相当于 *2 ②>> : 在一定范围内,每向右移1位,相当于 /…...
2024/4/13 4:15:55 - 我是我,那你呢
...
2024/4/20 5:51:44 - Spring漫画学习笔记(六) @Resource是如何工作的
...
2024/4/19 8:44:05 - 关于pytorch扩展的几个问题
一:cuda扩展 涉及到cuda扩展,有关cuda源码(.cu文件)编译的部分需要使用NVIDIA官网提供的CUDAtoolkit工具,一些工程下喜欢使用ninja来对源码进行编译,此时要注意ninja需要cuda的版本至少在10.0以上。&#x…...
2024/4/13 4:16:50 - 8. 商品管理---商品分类
1. 商品分类概述 商品分类用于在购物时,快速找到所要购买的商品,可以通过电商平台主页直观地看到。 2. 商品分类列表 实现基本布局实现分类列表数据加载 // 获取商品分类数据async getCateList () {const { data: res } await this.$http.get(categor…...
2024/4/18 7:07:40 - vue随记1 -- 插值语法和指令语法 el的写法 data的写法 MVVM模型 vue 的数据代理
b站教程:尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 vue随记11. 模板语法:2. 插值语法和指令语法:3. el的写法 -- 绑定实例的写法4. data的写法 -- vue实例中的数据5. MVVM模型6. vue 的数据代理1. 模板语法: 可以展示vue实…...
2024/5/5 19:51:48 - 《OpenMV图像处理方法》学习笔记 .3.使用统计信息
使用统计信息 要知道一个区域内的平均颜色或者面积最大的颜色——Statistics 1. ROI感兴趣的区域 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6y6fkYyY-1644756846905)(D:\openmv\图片\roi感兴趣的区域.png)] roi的格式是(x,y,w,h)的tupple …...
2024/4/17 8:38:08 - 猫影视TV 2.0.8 附稳定源地址
猫影视TV是一款非常不错的影视软件,这是一款非常不错的影视播放器软件,用户可通过加入各种影视源播放自己喜欢的精彩影视内容,在这里用户能够看到最为丰富的精彩影视,并且没有任何限制,可以不用观看广告,各…...
2024/4/16 11:59:51 - 部署hadoop+hive环境详解(window10环境)
文章目录一、安装JDK81)JDK下载地址2)设置环境变量3)验证二、Hadoop安装(window10环境)1)下载Hadoop3.1.32)Hadoop配置环境变量3)在hadoop解压目录下创建相关目录4)修改H…...
2024/4/13 4:16:40 - GIS资源网站
技术支持网站 osgeo中国中心 https://www.osgeo.cn/ 奥维互动帮助文档 https://www.ovital.com/help/ qgis学习 https://malagis.com/category/qgis-handbook-zh-cn/ ENVI技术殿堂 http://blog.sina.cn/dpool/blog/enviidl#type-1 ArcGIS ArcMap 编辑的入门教程 http://www…...
2024/4/20 4:48:43 - Spring漫画学习笔记(五) @Autowired是如何工作的
1.Autowired写在属性上 2.Autowired写在方法上 3.Autowired写在构造方法上...
2024/4/13 4:16:35 - 树与图的广度优先遍历(图中点的层次)
题目: 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue>using namespace std;const int N 100010;int n, m; int h[N], e[N], ne[N], idx; int d[N],q[N];//插…...
2024/4/18 6:58:01 - 源码系列第1弹 | 带你快速攻略Kafka源码之旅入门篇
大家过年好,我是 华仔, 又跟大家见面了。 从今天开始我将为大家奉上 Kafka 源码剖析系列文章,正式开启 「Kafka的源码之旅」,跟我一起来掌握 Kafka 源码核心架构设计思想吧。 今天这篇我们先来聊聊 Kafka 源码环境搭建、源码全景图以及后续…...
2024/4/15 7:55:58 - RHCSA第三天笔记
查看目录文件: ls 查看文本文件内容 cat 将文件内容输出到终端,查看文件内容(正序) -n 查看文件内容显示标号 tac 将文件内容输出到终端,查看文件内容(逆序显示) more 一般对于大文件查看通…...
2024/4/17 19:17:40 - 2022.02.13
优先队列是基于堆实现的,而堆则可以看作是完全二叉树。优先队列可以由很多种方式进行存储,如果用数组表示的话,则会由最大堆和最小堆的两种情况,但无论是哪种情况,在建堆的过程中,我们总需要话O(…...
2024/4/13 4:16:55 - 树莓派C++开发机器人智能小车(21)OpenCV调用树莓派摄像头
安装v4l2驱动器 OpeCV库默认识别插在RPiUSB口的USB摄像头,但不能直接探测RPi摄像头。为了识别RPi摄像头,我们需要在模块文件加载v412驱动。输入下面命令打开这个文件: sudo nano /etc/modules 要加载v412驱动,在下面文件里添加bcm2853-v412 按下Ctrl+O后回车保存文件,按…...
2024/4/28 23:47:26 - Type ‘java.util.Observable‘ does not have type parameters
错误: 解决方法:发现是导包的问题,导成了工具类下面的包,修改即可...
2024/4/13 4:17:05 - 【PAT】1126 Eulerian Path (25 分)
区别连通和相邻 #include <bits/stdc.h> using namespace std; //欧拉路径:访问且仅访问每个结点一次 //欧拉环:起点终点的欧拉路径 //Eulerian:连通图,所有结点偶数度 ->有一个欧拉环 //semi-Eulerian:两个结点奇数度,其中一个是起点,一个是终…...
2024/4/19 16:35:59 - JDBC与MYSQL数据库
加载MYSQL数据库驱动 本作者使用的开发工具是IntelliJ IDEA,其他集成环境也是类似的原理。 下载MYSQL数据库驱动 JDBC即(Java Data Base Connectivity),应用程序为了能访问MYSQL数据库服务器上的数据库,必须保证应用程序所驻留的计算机上安装有相应的…...
2024/4/18 16:21:18
最新文章
- python print 越来越优雅
在Python中,print() 是一个内置函数,用于将指定的字符串或变量的值输出到控制台,虽然简单,但却很有用,而且它一直在进步,我们来看看代码深切体会一下: # 打印简单的字符串 print("Hello, …...
2024/5/5 20:38:18 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - Go语言map、slice、channel底层实现(go面试)
slice 切片是一个引用类型,其底层实现是一个结构体,包含以下字段: ptr:一个指向底层数组的指针,指针指向数组的第一个元素。 len:切片当前包含的元素数量。 cap:切片的容量,即底层…...
2024/5/5 1:45:06 - LeetCode-46. 全排列【数组 回溯】
LeetCode-46. 全排列【数组 回溯】 题目描述:解题思路一:回溯。回溯三部曲解题思路二:0解题思路三:0 题目描述: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案…...
2024/5/2 11:58:51 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/4 23:54:56 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/4 23:54:56 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/4 23:55:17 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/4 23:55:16 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/4 18:20:48 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/4 23:55:17 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/4 23:55:06 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/4 23:55:06 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/5 8:13:33 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/4 23:55:16 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/4 23:55:01 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/4 23:54:56 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57