【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记
运行时类信息(RTTI)
C++:
##是拼接
#是替换成字符串
// RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <afxwin.h>#ifdef _DEBUG
#define new DEBUG_NEW
#endifCWinApp theApp;int main()
{//CListBox是MFC自带的控件类CListBox* pListBox = new CListBox;//GetRuntimeClass方法返回运行时类信息CRuntimeClass* pRuntimeClass = pListBox->GetRuntimeClass();std::cout << pRuntimeClass->m_lpszClassName << std::endl;//运行时类的IsDeriverFrome方法可以判断该类是否继承于某类if (pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CWnd))) {std::cout<<"CListBox类继承于CWnd类"<<std::endl;}if (pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CView))) {std::cout << "CListBox类继承于视图类" << std::endl;}//运行时类的m_pfnGetBaseClass方法可以获取父类的信息CRuntimeClass* pParentClass = pRuntimeClass->m_pfnGetBaseClass();if (pParentClass->IsDerivedFrom(RUNTIME_CLASS(CWnd))) {std::cout << "运行时类的父类继承于CWnd类" << std::endl;}//动态创建一个对象:CWnd* pWnd = (CWnd*)pParentClass->m_pfnCreateObject();return 0;
}
这是MFC提供的运行时类信息的使用,如果我们自己创建一个类,如果想用这些方法,必须要满足三个条件:
- 这个类必须继承于CObject类
- 类内必须声明DECLARE_DYNAMIC
- 类外必须实现IMPLENENT_DYNAMIC
我们来看看是如何实现的:
拆分宏
DECLARE_DYNAMIC(SHape)
public: //静态的结构体//本来是static const CRuntimeClass class##class_name;,拼接之后:static const CRuntimeClass classSHape; //虚函数virtual CRuntimeClass* GetRuntimeClass() const; //IMPLEMENT_DYNAMIC(SHape,CObject)
IMPLEMENT_RUNTIMECLASS(SHape, CObject, 0xFFFF, NULL, NULL)AFX_COMDAT const CRuntimeClass SHape::classSHape =
{ "SHape", sizeof(class SHape), 0xFFFF, NULL, RUNTIME_CLASS(CObject),//返回父类静态结构体的地址NULL,NULL
}; CRuntimeClass* SHape::GetRuntimeClass() const
{ return RUNTIME_CLASS(SHape);
}struct CRuntimeClass
{LPCSTR m_lpszClassName; //类名称int m_nObjectSize; //类大小UINT m_wSchema; //类版本CObject* (PASCAL* m_pfnCreateObject)(); //动态创建才会使用 暂时NULL函数指针CRuntimeClass* m_pBaseClass; //父类信息CRuntimeClass* m_pNextClass; //NULLconst AFX_CLASSINIT* m_pClassInit; //NULL
}#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
((CRuntimeClass*)(&SHape::classSHape))
}
这里给出RTTI的图,每一个类中都保存了这样一个结构,相当于链表,我们有当前的类信息,就可以得到所有父类信息:

动态创建机制
如果想在MFC中实现动态创建:
- 也必须继承与CObject类
- 类内声明DECLARE_DYNCREATE
- 类外实现IMPLEMENT_DYNCREATE
class SHape : public CObject
{
public:DECLARE_DYNCREATE(SHape)
};
IMPLEMENT_DYNCREATE(SHape,CObject)class CLine : public SHape
{
public:DECLARE_DYNCREATE(CLine)
};
IMPLEMENT_DYNCREATE(CLine, SHape)
需要注意的是,动态创建宏中包含了动态信息的宏
使用:
int main()
{HMODULE hModule = ::GetModuleHandle(nullptr);AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0);//定义直线类:CLine line;//方法内部this指针指向line//该方法用于判断是否继承与某个类if (line.IsKindOf(RUNTIME_CLASS(SHape))) {std::cout << "是图形" << std::endl;}if (line.IsKindOf(RUNTIME_CLASS(CWnd))) {std::cout << "是窗口" << std::endl;}else {std::cout << "不是窗口" << std::endl;}CObject* pLine = RUNTIME_CLASS(CLine)->CreateObject();if (pLine->IsKindOf(RUNTIME_CLASS(SHape))) {std::cout << "创建成功" << std::endl;}return 0;
}
动态创建包括了类信息
//函数跟踪
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{//拿到链表头节点CRuntimeClass* pClassThis = this->GetRuntimeClass();pClassThis->IsDerivedFrom(参数是判断的结构体地址){while (pClassThis != NULL){if (pClassThis == 参数)return TRUE;if (pClassThis->m_pfnGetBaseClass == NULL)break;//获取父类静态结构体地址pClassThis = pClassThis->m_pBaseClass;}}}#define DECLARE_DYNCREATE(class_name) \DECLARE_DYNAMIC(class_name) \static CObject* PASCAL CreateObject();CObject* PASCAL CLine::CreateObject() { return new CLine; } \IMPLEMENT_RUNTIMECLASS(CLine, SHape, 0xFFFF, class_name::CreateObject, NULL)AFX_COMDAT const CRuntimeClass class_name::class##class_name =
{ "CLine", sizeof(class CLine),0xFFFF, pfnNew, RUNTIME_CLASS(base_class_name), NULL,class_init
};
视图分割
CSplitterWnd:专门负责窗口切分
创建对话框,视图:Create函数
重载父类框架类的虚函数CFrandWnd::OnCreateClient,这个函数专门用于切分
动态创建:Create函数
静态创建:CreateStatic函数
-
静态分割(在窗口创建的时候就已经分割好了)
在我们的框架类中:
class CMyFrameWnd:public CFrameWnd{ public:CSplitterWnd spWnd;CSplitterWnd spWnd1;virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){spWnd.CreateStatic(this,2,1);spWnd1.CreateStatic(this,1,2,WS_CHILD|WS_CISIBLE,IdFromRowCol(0,0));return true;}} }我们这样写完了之后,发现运行不起来,这是因为我们只是创建了框架,但是没有创建视图
我们需要给视图类添加动态创建机制,我们重新写代码:
//我们自己的视图类: class MyView:public CView{DECLARE_DYNREATE(MyView);virtual void OnDrow(DCD* pDC){} } IMPLEMENT_DYNCREATE(MyView,CView) //我们自己的框架窗口类: class CMyFrameWnd:public CFrameWnd{ public:CSplitterWnd spWnd;CSplitterWnd spWnd1;virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){spWnd.CreateStatic(this,2,1);spWnd1.CreateStatic(&spWnd,1,2,WS_CHILD|WS_CISIBLE,spWnd,IdFromRowCol(0,0));//创建视图:spWnd1.CreateView(0,0,RUNTIME_CLASS(MyVIew),CSize(50,50),pContext);spWnd1.CreateView(0,1,RUNTIME_CLASS(CTreeView),CSize(50,50),pContext);spWnd.CreateView(1,0,RUNTIME_CLASS(CHtmlView),CSize(50,50),pContext);//设置行信息:spWnd.SetRowInfo(0,200,100);//设置列信息spWnd1.SetColumnInfo(0,200,100);spWnd1.SetColumnInfo(1,200,100);CHtmlView* html = (CHtmlView*)spWnd.GetPane(1,0);html->Navigate(L"e:/");return true;} }现在我们创建了三个窗口
这些控件都被MFC接管了,Win32 EDIT处理函数,微软定义的,
如果想要处理消息,就要使用Win32子类化(笔记在本篇最后面)
-
现在我们想处理树视图上的消息
-
Create方法:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs,CCreateContext* pContext){swWnd.CreateStatic(CWnd* pParentEnd,//分隔器窗口的父框架窗口句柄int nRows,//行数,这个值必须不超过16int nCols,//列数,这个值必须不超过16dwStyle,//指定窗口的风格nID//此窗口的ID,如果这个分隔器窗口不是嵌套在另一个分隔器窗口中的,则这个ID可以是AFX_IDW_PANE_FIRSH) -
获取窗口ID:CSplitterWnd::IdFromRowCol方法:
int IdFromRowCol(int row,int col);//参数:行数,列数 返回值:返回此窗格的子窗口的ID -
在分隔器中创建一个窗格:CreateVIew方法:
virtual BOOL CreateView(int row,int col,CRuntimeClass* pVIewClass,SIZE sizeInit,CCreateContext* pCOntext); 参数:行数,列数,运行时类信息,初始尺寸,用来创建此试图的创建环境的指针
-
-
动态分隔:
//我们自己的视图类: class MyView:public CView{DECLARE_DYNREATE(MyView);virtual void OnDrow(DCD* pDC){} } IMPLEMENT_DYNCREATE(MyView,CView) //我们自己的框架窗口类: class CMyFrameWnd:public CFrameWnd{ public:CSplitterWnd spWnd;CSplitterWnd spWnd1;virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){CCreateContext Context;Context.m_pNewViewClass = RUNTIME_CLASS(MyView);spWnd.Create(this,2,2,CSize(50,50),&Context);return true;} }- 动态切分,Create方法:
BOOL CSplitterWnd::Create(CWnd* pParentWnd,//分隔器父窗口的句柄int nMaxRows,//分隔器窗口的最大行数,这个值不能超过2int nMaxCols,//分隔器窗口的最大列数,这个值不能超过2SIZE sizeMin,//指出显示一个窗格所需的最小尺寸CCreateCOntext* pContext,//指向一个CCreateContext结构的指针DWORD dwStyle = WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|SPLS_DYNAMIC_SPLIT,//窗口风格UINT nID = AFX_IDW_PANE_FIRST//此窗口的子窗口ID。如果这个分隔器窗口不是嵌套在另一个分隔器窗口中的,则这个ID可以是AFX_IDW_PANE_FIRST)
- 动态切分,Create方法:
Win32子类化
在win32编辑框,可以设置属性,风格,字母不可见,****等
现在有一个需求:只允许输入小写字母和数字
C++中,只要继承,重写虚函数,在交给父类处理
在Win32中,添加一个编辑框,和一个按钮,
添加一个全局变量,保存原来的edit消息处理函数
WNDPROC OldProc;
然后添加按钮回调函数:
LRESULT CALLBACK MyProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam){if(uMsg == WM_CHAR){if(wParam>='a'&&wParam<='z'||wParam>='0'&&wParam<='9'){//交给原来的处理函数处理return OldProc(hDlg,uMsg,wParam,lParam);}else{return 0;}}
}
消息处理函数中:
if(LOWORD(wParam)==IDC_BUTTON1){//要先找到窗口句柄:HWND hEdit = GetDlgItem(hDlg,IDC_EDIT1);//修改原来的过程函数(原来是操作系统默认的//OldProc = (WNDPROC)SetWindowLogn(hEdit,GWL_WNDPROC,(LONG)MyProc);//第二种方式,之前创建的编辑框不好使,但是之后创建的对话框,可以使用//这个子类化方式实际上是改了类的回调,就是EDIT类的回调,之后创建的EDIt类就好使了OldProc = (WNDPROC)SetWindowLogn(hEdit,GCL_WNDPROC,(LONG)MyProc);
}
第二种方式:
再添加一个按钮
HISRANCE hInst;
消息回调:
if(LPWORD(wParam)==IDC_BUTTON2){CreateWindow(L"EDIT","ads",WS_CHILD|WS_VISIBLE,100,100,100,100,hDlg,NULL,hInst);
}
MFC子类化
这里我们把第一个视图换成一个窗口(对话框)
- 创建一个对话框
- 为该对话框添加一个类:CMyFormView
- 主cpp中,包含刚才添加的类的头文件
- 然后把第一个改为我们创建的类
//我们自己的视图类: class MyView:public CView{DECLARE_DYNREATE(MyView);virtual void OnDrow(DCD* pDC){} } IMPLEMENT_DYNCREATE(MyView,CView) //我们自己的框架窗口类: class CMyFrameWnd:public CFrameWnd{ public:CSplitterWnd spWnd;CSplitterWnd spWnd1;virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){spWnd.CreateStatic(this,2,1);spWnd1.CreateStatic(&spWnd,1,2,WS_CHILD|WS_CISIBLE,spWnd,IdFromRowCol(0,0));//创建视图:spWnd1.CreateView(0,0,RUNTIME_CLASS(MyFormView),CSize(50,50),pContext);spWnd1.CreateView(0,1,RUNTIME_CLASS(CTreeView),CSize(50,50),pContext);spWnd.CreateView(1,0,RUNTIME_CLASS(CHtmlView),CSize(50,50),pContext);//设置行信息:spWnd.SetRowInfo(0,200,100);//设置列信息spWnd1.SetColumnInfo(0,200,100);spWnd1.SetColumnInfo(1,200,100);CHtmlView* html = (CHtmlView*)spWnd.GetPane(1,0);html->Navigate(L"e:/");return true;} } - 需要把我们创建的对话框修改为子窗口类,否则会报错
-
DDX/DDV虚函数:
就是空间绑定变量/数据,调用UpdataTA(),交互数据用
控件绑定变量
值绑定变量
相关文章:
【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记
运行时类信息(RTTI) C: ##是拼接 #是替换成字符串 // RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <afxwin.h>#ifdef _DEBUG #define new DEBUG_NEW #endifCWinApp th…...
ui设计师个人年终工作总结2020最新范文5篇
ui设计师个人年终工作总结(一) 我于20年x育人x日成为公司的试用员工,安排在技术研发中心担任UI界面设计一职,现将我的工作情况分为三个阶段来总结如下汇报: 一、萌芽阶段 记得初次应聘时,我对公司的认识仅仅局限于行业之一&#x…...
开源数据库Mysql_DBA运维实战 (修改root密码)
MySQL——修改root密码的4种方法 本文以windows为例为大家详细介绍下MySQL修改root密码的4种方法,大家可以可以根据的自己的情况自由选择,希望对大家有所帮助 方法1: 用SET PASSWORD命令 首先登录MySQL。 格式:mysql> set pass…...
javap获取Kotlin方法JNI方法签名
获取Kotlin方法签名和JAVA不一样的地方就是需要使用Kotlin 命令行编译器生成.class文件: 编写一个Kotlin类,添加JNI方法: class TestLib {external fun init(callBack: CallBack)interface CallBack{fun onData(count:Int,data:String)} }在…...
ARM-M0内核MCU,内置24bit ADC,采样率4KSPS,传感器、电子秤、体脂秤专用,国产IC
ARM-M0内核MCU 内置24bit ADC ,采样率4KSPS flash 64KB,SRAM 32KB 适用于传感器,电子秤,体脂秤等等...
【STM32】FreeRTOS消息队列和信号量学习
一、消息队列(queue) 队列是一种用于实现任务与任务之间,任务与中断之间消息交流的机制。 注意:1.数据的操作是FIFO模式。 2.队列需要明确数据的大小和队列的长度。 3.写和读都会出现堵塞。 实验:创建一个消息队列…...
初始C语言(6)——详细讲解表达式求值以及其易错点
系列文章目录 第一章 “C“浒传——初识C语言(1)(更适合初学者体质哦!) 第二章 初始C语言(2)——详细认识分支语句和循环语句以及他们的易错点 第三章 初阶C语言(3)——…...
【数据结构】树和二叉树
一、树的概念及结构 1、树的概念 树 是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 有一个特殊的结点&a…...
GPIO 配置 和 PINCTRL有啥区别
GPIO(通用输入/输出)和 PINCTRL(引脚控制器)是在嵌入式系统中用于管理和控制硬件引脚的关键概念。它们在硬件层面上起着不同的作用。 GPIO配置: GPIO 是一种通用的硬件接口,用于控制和读取数字信号。每个 …...
GPT法律领域
法律领域 LaWGPT Github: https://github.com/pengxiao-song/LaWGPT 简介:基于中文法律知识的大语言模型。 数据:基于中文裁判文书网公开法律文书数据、司法考试数据等数据集展开,利用Stanford_alpaca、self-instruct方式生成对话问答数据…...
【C++11保姆级教程】Type aliases(类型别名)、alignof and alignas(类型对齐))
文章目录 前言一、类型别名(Type aliases)1.1类型别名是什么?1.2使用方法1.3实际使用1.4优势 二、类型对齐(alignof and alignas)2.1类型对齐的概念2.2类型对齐快速理解2.3具体使用2.4示例代码 总结 前言 在C11标准中…...
地址解析协议-ARP
ARP协议 无论网络层使用何种协议,在实际网络的链路上传输数据帧时,最终必须使用硬件地址 地址解析协议(Address Resolution Protocol,ARP):完成IP地址到MAC地址的映射,每个主机都有一个ARP高速缓…...
Java线程
文章目录 一、Thread类1.1创建线程1.2Thread类中的一些构造方法1.3Thread类中的一些属性1.4线程的终止/打断1.5线程等待1.6获取当前线程的引用1.7休眠当前线程 二、线程的状态 一、Thread类 线程是操作系统的概念,操作系统内核实现了线程这样的机制,系统…...
C语言如何实现DES加密与解密
C语言实现DES加密解密 #include "des.h" //移位表 static Table_size const shiftTable[NumberOfKeys] {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; //E扩展表 static Table_size const eTable[des_key_pc2_standard]{32, 1, 2, 3, 4, 5, 4, 5, 6, …...
【笔记】优先队列(priority_queue/set)
目录 大根堆 小根堆 set(小根堆) 大根堆 题目链接:洛谷 P3243 菜肴制作 题目描述 知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。ATM 酒店为小 A 准备了 n 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1 到…...
看看安森美深力科NSI45090JDT4G 是如何点亮汽车内外照明系统解决方案
关于线性恒流调节器(CCR):是一种用于控制电流的稳定输出。它通常由一个功率晶体管和一个参考电流源组成。CCR的工作原理是通过不断调节功率晶体管的导通时间来维持输出电流的恒定。当输出电流超过设定值时,CCR会减少功率晶体管的导…...
Linux进阶之Shell-sed
基本用法: sed 选项 “指令” 文件 常用选项: -e --它告诉sed将下一个参数解释为一个sed指令,只有当命令行上给出多个sed指令时使用 -f --后跟保存了sed指令的文件 -i --直接对内容进行修改,不加 i 时默认只是预…...
前端高频面试题 Day02
面试题 var 和 let const 的区别 var 是 ES5 及之前的语法,let const 是 ES6 语法var 和 let 是变量,可修改;const 是常量,不可修改var 有变量提升,let const 没有var 没有块级作用域,let const 有 &…...
MYSQL完全卸载、安装与账号创建、权限控制
一、卸载mysql CentOS 卸载 MySQL 1. 查看安装情况 使用以下命令查看当前安装mysql情况,查找以前是否装有mysql rpm -qa|grep -i mysql这里显示我安装的 MySQL 服务有有: 2. 停止 mysql 服务、删除之前安装的 mysql 删除命令:rpm -e –n…...
get与post如何拼接url与数据的灵活处理,循环的重要性。
get与post拼接url地址不同: let postData {method: "post",data: {op: "/api/setting/maintenanceperiod?period"this.authorizationCode,loadingConfig: {},data: {period:this.authorizationCode}}}; if(this.editData.id){let postData …...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
