• 实现垫片类上


        最近读潘爱民先生翻译的《COM本质论》,看到了一个新名词“垫片类”(不要骂我老土,我真的是第一次见..),用来实现类型的转换。作者实现了一个_UNCC的垫片,实现了从TCHAR字符串到wchar_t类型字符串的转换。看一下原书中的例子:
    HRESULT IIDFromHWND(HWND hwnd, IID& riid)
    {
     TCHAR szEditText[1024];
     GetWindowText(hwnd, szEditText, 1024);
     return IIDFromString(_UNCC(szEditText), &riid);
    }

        其中最后一个函数的原形是:

     HRESULT IIDFromString(wchar_t *, GUID *);

        而_UNCC的作用,就是把第一个参数从TCHAR*类型转换成wchat_t*类型。

        抛开其他的东西不管,我们来分析一下_UNCC到底做了什么事。_UNCC的参数szEditText是一个TCHAR类型字符串,而返回的是一个wchar_t类型的字符串。这让_UNCC看起来像一个函数。现在让我们试着来实现这个函数。

        实现的方法决定于这个返回的wchar_t类型的字符串是放在内存哪部分的。首先我们假定是在栈区:

    wchar_t* _UNCC(TCHAR* s)
    {
        wchar_t wsz[100];
        Translate(wsz, s)///这里假定Translate是转换函数
        return wchar_t;
    }

        看起来是没有错的,也许你也能得到正确的结果,但是并不是每次都能正确,因为你返回的是一个指向栈区数据的指针,而栈内的数据在函数结束后就是无效的。要保持数据有效,需要把数据放到静态区,因此函数可以改成这样:

    wchar_t* _UNCC(TCHAR* s)
    {
        static wchar_t wsz[100];
        Translate(wsz, s)///这里假定Translate是转换函数
        return wchar_t;
    }

       这样解决了上述问题,但是也不是很好的。假如你的输入参数有200个字符,那个这个程序就崩溃了。也许可以把数组定义的大一些,比如1000,或者10000。但是仍然不能保证是够用的。而且,最重要的是,占用的静态区空间,在函数结束以后也不能归还给系统!!那试着在堆上分配合适的空间来实现:

    wchar_t* _UNCC(TCHAR* s)
    {
        wchar_t *wsz = new wchar_t[tcharstrlen(s) + 1];///这里假定tcharstrlen是TCHAR*类型的取长度函数
        Translate(wsz, s)///这里假定Translate是转换函数
        return wchar_t;
    }

       但是这样导致了一个更加烦人的内存泄露问题,你分配的空间谁去释放?

       现在是否会想,如果_UNCC是类就好了,我可以在析构函数里去delete。然后就会想到,_UNCC很可能是一个函数对象,那让我们试着用函数对象实现:

    class _UNCC
    {
    public:
     _UNCC():m_p(NULL){}
     ~_UNCC()
     {
      if (NULL != m_p)
      {
       delete[] m_p;
       m_p = NULL;
      }
     }
     wchar_t* operator()(TCHAR* s)
     {
      if (s != NULL)
      {
       m_p = new wchar_t[strlen(s) +1];
       Translate(m_p, s)///这里假定Translate是转换函数
      }
      else
      {
       m_p = new wchar_t[1];
       *m_p = 0;
      }
      return m_p;
     }
    private:
     wchar_t* m_p;
    }

        太好了!这样能完全解决上面出现的所有问题,让我们用一下试试吧:
     ...
     return IIDFromString(_UNCC(szEditText), &riid);
        编译不通过!因为函数对象也是类,类需要先实例化的,改一下吧:
     ...
     return IIDFromString(_UNCC()(szEditText), &riid);
        能用是能用了,但是和原函数的形式不一样,而且这种形式让人感到很别扭。解决这个问题也很简单,用#define把()取代掉!修改以后的函数看起来这样:

    class UNCC
    {
    public:
     UNCC():m_p(NULL){}
     ~UNCC()
     {
      if (NULL != m_p)
      {
       delete[] m_p;
       m_p = NULL;
      }
     }
     wchar_t* operator()(TCHAR* s)
     {
      if (s != NULL)
      {
       m_p = new wchar_t[strlen(s) +1];
       Translate(m_p, s)///这里假定Translate是转换函数
      }
      else
      {
       m_p = new wchar_t[1];
       *m_p = 0;
      }
      return m_p;
     }
    private:
     wchar_t* m_p;
    };

    #define UNCC() _UNCC

        这样就完全实现了这个垫片类,现在可以放心的使用了。
     ...
     return IIDFromString(_UNCC(szEditText), &riid);
        在调用这个函数的时候,首先生成一个临时的UNCC类对象实例,然后通过此实例调用重载过的()操作符来实现类型转换,并返回指针给调用他的函数使用。整个函数结束以后,临时UNCC的作用域结束,UNCC析构函数把动态分配的空间释放。

        下面是一个垫片类的完整例子,垫片_A()实现了从string类型,char*类型和整数类型到字符串类型的转换,并在PrintStr函数中调试。

    #include <iostream>
    using namespace std;

    class A
    {
    public:

     A():m_p(NULL)
     {
     }

     ~A()
     {
      if (NULL != m_p)
      {
       delete[] m_p;
      }
     }

     char* operator()(string s)
     {
      m_p = new char[s.length() + 1];
      strcpy(m_p, s.c_str());
      return m_p;
     }

     char* operator()(const char *s)
     {
      if (NULL == s)
      {
       m_p = new char[1];
       *m_p = 0;
      }
      else
      {
       m_p = new char[strlen(s) + 1];
       strcpy(m_p, s);
      }
      return m_p;
     }

     char* operator()(long s)
     {
      m_p = new char[9];
      sprintf(m_p, "%ld", s);
      return m_p;
     }

    private:

     char* m_p;

    };

    #define _A A()

    void PrintStr(const char* s)
    {
     cout<<s<<endl;
    }

    int main(int argc, char* argv[])
    {
     string s("I'm a C++ string");

     PrintStr(_A(s));

     PrintStr(_A("I'm a C string"));

     PrintStr(_A(38385438));

     return 0;
    }

    闲人,2005年10月6日

    下已经写好了:实现垫片类--下

  • 相关阅读:
    url传递参数带 + ,解决办法
    操作系统——内存地址重定位
    算法——二分查找变形题
    Java——代码性能优化
    maven——添加插件和添加依赖有什么区别?
    JavaWeb——Servlet如何调用线程池中的线程?
    「ZJOI2016」小星星
    [十二省联考2019]字符串问题
    [十二省联考2019]春节十二响
    [十二省联考2019]异或粽子
  • 原文地址:https://www.cnblogs.com/freeman/p/249271.html
Copyright © 2020-2023  润新知