zoukankan      html  css  js  c++  java
  • COM新手使用中一个易混淆的问题

    CSDN贴地址:

    http://blog.csdn.net/noslopforever/article/details/7278355

    其实也没什么,[don box]里面也提过这个问题,但是没有继续展开。

    比如依照图形系统而言,一般封装时,接口可能会这么来设计:

    interface IRenderObject{};

    interface IRenderResource: public IRenderObject {};

    interface IRenderTexture : public IRenderResource {};

    实现时,所有的Render Object您都希望将其绑定到Device上,所以您一定会希望有一个Render Object的公共基类来管理链表之类的。于是就可能这样:

    class MyObjectBase : public IRenderObject

    {

    MyObjectBase* m_pNext;

    };

    然后,为了记录资源的大小等信息,同理:

    class MyResource

    : public MyObjectBase

    , public IRenderResource

    {

    uint m_unSize;

    };

    最后,实现了一个Texture:

    class MyTexture

    : public MyResource

    , public IRenderTexture

    {

    GLuint m_hTexture;

    };

    ok,准备工作完成。

    接下来,使用者拿到这个库后,可能会通过这个系统的一个Facade创建一个Texture资源:

    IRenderTexture* texture = XXXXSystem()->CreateTexture();

    然后,系统同时提供了一个资源管理的很好用,他想把这个用到资源管理系统中,于是他:

    XXXXSystem()->ManagerResource(  texture  );

    这里,因为ManagerResource也是在XXXXSystem里提供的,为了实现功能,系统的提供者可能会这么写:

    void ManagerResource(IRenderResource* InResource)

    {

    MyResource* resource = static_cast<MyResource*>(InResource);

    resource->InnerMethod();

    resource->InlineMethod();

    }

    好了。

    到这里,您可以先考虑一下,现在会发生虾米事情?

    最好的情况——Crash。最差的情况——没有Crash,但是内部完全乱套了。

    为什么?

    我们看一下,这中间我们一直使用的是MyTexture的实例,它的内存布局如何呢?

    4字节vtbl

    4字节 void* m_pNext

    4字节IRenderResource vtbl

    4字节 uint m_unSize

    4字节IRenderTexture vtbl

    4字节 GLuint m_hTexture;

    也就是说,

    IRenderTexture* texture = XXXXSystem()->CreateTexture();

    这句话返回的是这个实例从头往下的第16个字节(0起始)。

    而且,最糟糕的是,在下面这一句中:

    XXXXSystem()->ManagerResource(  texture  );

    因为IRenderTexture同时“是一个”IRenderResource,所以,这个+16会被直接当做IRenderResource传入给ManagerResource。

    但事实上,按照ManagerResource的实现,它所希望的并非+16的IRenderTexture所包含的那个IRenderResource,而是+8的IRenderResource本身:

    MyResource* resource = static_cast<MyResource*>(InResource);

    这句话所做的,是把InResource的指针地址-8,如果传入的是+8的IRenderResource,它正好索取到这个对象的起始位置,一切就都正常了。但是,我们传入的事实上却是+16,于是——

    程序发生了未可预知的错误,请与提供者或者微软联系……

    这个问题怎么解决呢?

    虽然[Don Box]里没有讨论这个情况,但却讨论了一个跟这个相关的主题,最后有一个原则性的结论,请千万要记住:

    接口不是C++指针!!

    因此:IRenderTexture接口就是IRenderTexture接口,它不能被当做IRenderResource接口使用,它里面所包含的IRenderResource的部分,只是说明

    “我Render Texture也具有这些部分的功能”。

    但并不代表C++意义上的:“我Render Texture同时也是一个Render Resource”。

    所以,如果遇到这种情况,应该这么做:

    IRenderTexture* texture = XXXXSYstem()->CreateTexture();

    ...

    IRenderResource* resource = (IRenderResource*)texture->QueryInterface(IID_IRenderResource);

    if (resource){

    XXXXSystem()->ManagerResource(resource);

    resource->Release();

    }

    这样就完全没有问题了。

    题外话:

    用Direct3D,总得接触一些COM,当时初学的时候,啥都喜欢追根究底,还真搬弄着Don Box的《Com本质论》猛读了一阵,后来发现工作中根本没啥用途,Direct3D那能叫COM吗?只是一些连皮毛都不算的东西,每本书还都煞有介事地用这个概念来唬人。Direct3D那些所谓接口云云,跟其它C++API库没什么不同,QueryInterface您用么?不用吧。Marshal什么的您用么?也不用吧。什么“接口并非指针”的问题,咱们也不会关注吧?若非必要,dxguid.lib估计很多人都不会去装载。其实COM的概念比起Direct3D用的程度要复杂得多,要不微软也就不至于去推.NET了——COM写起来太累了啊!!!!!

    一开始总觉得COM只是一个“更好地C++”,其实也提不上更好,因为很多C++好用的东西在COM中是无从体现的,而单纯以扩展性而言,比起具备强制二进制标准的纯C又好不了到哪去。不过后面慢慢习惯了COM那套概念以后,发现确实还是有好处的,不需要再回去写纯C,也不需要因此把很多本来很容易明白的概念封装成大量的函数和Handle,调用起来也很清晰,不会出现我把Texture Handle给扔到设置Vertex Buffer Object的地方。难了实现者,便宜了使用者(当然比起纯C++又不便宜,但是扩展性更好)。

  • 相关阅读:
    android音量知识总结
    android设置dialog透明度,黑暗度的方法
    获得图片资源总结
    Fragment使用案例
    activity主窗口与软键盘的交互模式
    AndroidManifest.xml中android:configChanges的简介
    2G,3G和4G网络类型了解
    安卓权限大全
    Spring
    多线程
  • 原文地址:https://www.cnblogs.com/noslopforever/p/2362792.html
Copyright © 2011-2022 走看看