关于 ATL 中 CComControl 的构造
分享一篇 C++语言 & ATL 的高阶解读笔记,你需要在C++语言特性中上串下跳,应该算篇有质量的文章。
class ATL_NO_VTABLE CHello :
// ...
public CComControl<CHello>
{
// ...
}
叶子类,继承CComControl,将自己作为模板参数传入。(ATL贯彻全场的静态多态技巧)
template <class T, class WinBase = CWindowImpl< T > >
class ATL_NO_VTABLE CComControl : public CComControlBase, public WinBase
{
public:
CComControl() : CComControlBase(m_hWnd) {}
// ...
}
CComControl有个默认模板参数CWindowImpl,而CWindowImpl也使用叶子类作为其模板参数。
在初始化列表中CComControlBase使用的m_hWnd来得有点妖怪:
m_hWnd是WinBase带来的(多重继承)
m_hWnd这时候没有 有效的值
因为 子对象CComControlBase 先于 子对象WinBase构造,而 子对象WinBase 只是分配了内存(见下条解释)
这里违背了“按声明次序使用”的原则,使用了没有初始化的成员,进入危险地带。
m_hWnd这时候 有 有效的内存空间。
因为叶子对象CHello已经分配了空间,正在通过 构造函数 建立各成员变量的初值。
尽管CComControlBase使用了一个没有初始化的、叶子类带来的成员变量,但小心使用依然有效。
class ATL_NO_VTABLE CComControlBase
{
public:
CComControlBase(HWND& h) : m_hWndCD(h)
{
memset(this, 0, sizeof(CComControlBase));
m_phWndCD = &h;
// ...
}
// ...
// Attributes
public:
#pragma warning(push)
#pragma warning(disable: 4510 4610) // unnamed union
union
{
HWND& m_hWndCD;
HWND* m_phWndCD;
};
#pragma warning(pop)
// ...
}
这里进入CComControlBase神奇的构造函数
它将自己(作为一个子对象)所拥有的内存清零:起始地址this,清零长度自己的大小sizeof(CComControlBase)
注意它的参数是一个引用,这里 传值 与 传引用 将有迥然不同的区别(而不是过去的差不多,主要是优化)
m_phWndCD赋值为 参数的地址。语言特性小考场:由于参数是引用,引用的取地址值,与引用所值对象地址一致。
回到CComControl构造函数初始化列表问题,子对象CComControlBase的m_phWndCD现在指向,另一个子对象WinBase中的m_hWnd成员。
设计讨论:CComControlBase为使用一个HWND参数,没有进一步使用模板,而是简单的使用一个成员变量。虽然CComControlBase子对象和WinBase子对象都住在一个家中——CHello叶子对象里,CComControlBase应有可能使用模板,进一步的将m_phWndCD所占内存开销去掉。为啥没有呢?是为了避免过多的纠缠、变复杂化(以易于独立思考、理解),还是HWND需要一定的独立性?
语言特性弯弯绕绕:再遇union的效用
语言书上说“引用一经复制,便不能修改”,幽暗的世界里这不是事实。此处应该使用了语言实现的一些底层特性,以及Windows编程时的一些命名约定。
匿名union让m_hWndCD与m_phWndCD共用一个内存,通过对m_phWndCD的修改,我们改变了引用m_hWndCD的值!
介于Windows编程的命名约定,我们习惯了m_pXXX == & m_XXX 这样的惯例,此处同样成立
&m_hWndCD == m_phWndCD
*m_phWndCD == m_hWndCD
有点抓狂,对同一个内存的两种访问方法,居然得到相同的值,更有甚者 m_hWndCD == m_hWnd,这绝对是对C++语言意志力的考验!
关于 ATL 中 CComControl 的构造的更多相关文章
- ATL中窗口句柄与窗口过程的关联方法
ATL中采用了一种动态生成机器指令的方式进行窗口句柄与窗口对象进行关联,以是详细分析: CWindowImpl会在第一次调用Create时注册窗口类,该窗口类是的信息是在CWindowImpl的子类中 ...
- Scala 深入浅出实战经典 第81讲:Scala中List的构造是的类型约束逆变、协变、下界详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-97讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- 理解ATL中的一些汇编代码(通过Thunk技术来调用类成员函数)
我们知道ATL(活动模板库)是一套很小巧高效的COM开发库,它本身的核心文件其实没几个,COM相关的(主要是atlbase.h, atlcom.h),另外还有一个窗口相关的(atlwin.h), 所以 ...
- 第81讲:Scala中List的构造和类型约束逆变、协变、下界详解
今天来学习一下scala中List的构造和类型约束等内容. 让我们来看一下代码 package scala.learn /** * @author zhang */abstract class Big ...
- [Leetcode] Construct binary tree from preorder and inorder travesal 利用前序和中续遍历构造二叉树
Given preorder and inorder traversal of a tree, construct the binary tree. Note: You may assume tha ...
- ATL中宏定义offsetofclass的分析
近日学习ATL,通过对宏定义offsetofclass的解惑过程.顺便分析下虚函数表,以及通过虚函数表调用函数的问题. 1 解开ATL中宏定义offsetofclass的疑惑 #define _ATL ...
- LeetCode105 从前序和中序序列构造二叉树
题目描述: 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9 ...
- Vue中的Xss构造
首发tools:https://www.t00ls.net/thread-59512-1-1.html 存储型XSS 最近做测试的时候碰到了一个前端页面使用了Vue框架的项目 在测试XSS漏洞的过程中 ...
- 在Asp.Net MVC中使用ModelBinding构造Array、List、Collection以及Dictionary
在asp.net mvc中,我们可以在html表单中使用特定的格式传递参数,从而通过model binder构造一些集合类型. 第一种方式 public ActionResult Infancy(Pe ...
随机推荐
- Python学习之协程
8.8 协程 我们都知道线程间的任务切换是由操作系统来控制的,而协程的出现,就是为了减少操作系统的开销,由协程来自己控制任务的切换 协程本质上就是线程.既然能够切换任务,所以线程有两个最基本的 ...
- noi.ac-CSP模拟Day5T2 灯
算是一道思维题吧,没有什么算法在里面. 之前想的是,能走的话就尽量走远,走过去开灯然后再回去关灯,然后再走,每一段路要走3次. 然而,“能走的话就尽量走远”只是yy的一个贪心,没有任何依据.假设在中间 ...
- 第七周课程总结&实验报告
课程总结 主要学习了抽象类与接口的应用 1.抽象类的成员可以具有访问级别 接口的成员全部public级别 2.抽象类可以包含字段 接口不可以 3.抽象类可以继承接口 接口不能继承抽象类 4.抽象类的成 ...
- RSA加密 抛异常 algid parse error, not a sequence
JDK1.8环境 参考:BouncyCastle的使用:https://blog.csdn.net/qq_29583513/article/details/78866461 可解决 公钥解密 私钥加密 ...
- [转帖]RSA算法与DSA算法的区别
RSA算法与DSA算法的区别 https://cloud.tencent.com/developer/news/254061 文章来源:企鹅号 - SuperFullStack 本文译自:StackE ...
- 什么是SQL注入以及mybatis中#{}为什么能防止SQL注入而${}为什么不能防止SQL注入
1.什么是SQL注入 答:SQL注入是通过把SQL命令插入到web表单提交或通过页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL指令. 注入攻击的本质是把用户输入的数据当做代码执行. 举例如: ...
- 拼音检查python
#coding=utf-8 #!/usr/bin/python import sys, re, collections #读入文件 def read_file(filename): try: fp = ...
- java学习笔记 - 线程池(一)
线程池(Thread Pool):把一个或多个线程通过统一的方式进行调度和重复使用的技术,避免了因为线程过多而带来使用上的开销 优点:(面试题)可重复使用已有线程,避免对象创建.消亡和过度切换的性能开 ...
- 利用pcl数据结构,实现RegionGrowing的复现
这篇博客是pcl中区域增长的算法进行简介以实现重写,并添加了一些判断条件. 起初原因是在使用pcl封装的regionGrowing时,效果不太好. 于是想自己重新写一下,通过改变其中种子点的生成策略和 ...
- [CF750G] New Year and Binary Tree Paths
目录 简单的 组合的 题目链接 简单的 设从节点\(x\)开始不断往左儿子走h-1步,则编号和为\(x\sum_{i=0}^{h-1}2^i=x(2^h-1)\). 若倒数第\(i\)步走向的是右儿子 ...