• 关于 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++语言意志力的考验!

    本文转载处:http://blog.sina.com.cn/s/blog_843c41c80101h3x9.html

  • 相关阅读:
    谢尔宾斯基三角形,“混沌游戏”实现 20141022
    Who are you, What is the science
    The Tao to Excellent 2
    Mac Mini Server安装Centos6.5
    关于ftp的功能类——下载,上传,断点,连接
    mysql http://yaojialing.iteye.com/blog/773973
    序列号
    JS 文件复制
    java MySQLFront_Setup
    牛人博客
  • 原文地址:https://www.cnblogs.com/ice-arrow/p/11726361.html
Copyright © 2020-2023  润新知