3DExperience:创建组件和使用智能指针

以前用V5的时候,对组件、接口以及智能指针一知半解,能用,但是理解的不是很透彻,现在在学V6,把百科全书仔细看了一下,好好的理解了一下组件、接口和智能指针

创建组件

什么是组件

组件是构建应用程序的元素。组件是一段你无法修改的可执行代码,但是你可以通过它公开的接口来使用,它隐藏了它的实现细节,即使在运行时也可以由暴露它的另一个组件替换。接口和执行相同的工作,并确保向上兼容使用它的客户端应用程序。
对象提供组件所需的功能
1、对象可以公开其接口并隐藏其实现
2、对象可以在运行时与其他匹配相同接口的对象进行交换,即使它们由位于网络其他位置的对象服务器提供,也可以提供组件模块化
3、可以提供具有新功能的新版本的对象,同时保持客户端应用程序无需重建即可运行,从而提供组件可伸缩性。

什么是CAA组件

根据我的理解,CAA组件就是由一堆类和接口组成的整体,类都继于某一基类(CAA中为CATBaseUnknown),组件中的类之间可以相互查询得到,类实现接口。当一个组件的实例生成时,组件中的类也生成相应的实例。

圆的组件模型

上图为一个圆的组件,当您需要创建一个圆时,您可以调用circle component factory,它创建一个circle实例并返回一个指向CATICircle接口的指针。然后需要移动圆圈,并调用QueryInterface来为这个圆组件实例获取指向CATIMove接口的指针。你也可能需要绘制它,或者修改它的显示属性,当需要对这个圆操作时,如果您还没有获取到合适的接口,需要先获得(用QueryInterface)一个指向这个接口的指针,然后通过这个接口指针来调用相应的函数操作圆。
物理视图设计和编写单个应用程序组件(如圆形)作为多个C ++类,一个是圆的主实现类,以及此主实现类的其他扩展类,每个类实现一个或多个接口,客户端应用程序程序员将通过它来操作圆。

什么是扩展类

主实现类的扩展是一个单独的类,它附加到主实现类,并扩展它以实现主实现类本身没有实现的其他接口。扩展的常见用法是共享多个组件之间的接口的实现,或者实现一个新的接口,该接口是在发布框架时没有的以及后面没有新添加的。这对模块化和可扩展性有很大帮助。
扩展类是一种C ++类,它必须是直接CATBaseUnknown类派生。它们可以是以下类型
1、数据扩展,包含数据成员和方法。
2、代码扩展,只包含方法而没有数据成员。
扩展类与它扩展的类是使用宏CATImplementClass在扩展源文件中声明的。扩展和它实现的接口之间的链接通过字典进行管理。包含扩展代码的共享库也需要包含在里面。
当用QueryInterface请求指向扩展实现的接口的指针时,会自动创建实现的接口的指针作为组件扩展的类。

数据扩展

数据扩展
新接口CATIData由单独的C ++类MyDataExtension实现,其中包含访问数据和存储数据的方法。
生命周期:MyObject实现的接口调用QueryInterface创建数据扩展的实例或已有现有实例。当主类对象及其所有扩展对象的所有引用计数都达到0时,才会删除数据扩展对象,所有的扩展对象是与主类以及它的扩展对象同时消亡的。
CATIData的idl文件如下:

// IDL encoded interface

#pragma ID CATIData "DEC:7db286f1-218d-0000-0280020a86000000"
interface CATIData : CATBaseUnknown {
#pragma PROPERTY Length
  HRESULT get_Length(out int oLength);
  HRESULT put_Length(in int iLength);
};

CATIData的头文件如下:

// C++ generated interface class header file
#include "CATBaseUnknown.h"
extern IID IID_CATIData;
class CATIData : CATBaseUnknown {
  CATDeclareInterface;
  public :
    virtual HRESULT __stdcall get_Length(int * oLength) = 0;
    virtual HRESULT __stdcall set_Length(int iLength) = 0;
};

CATIData的源文件如下:

// C++ generated interface class source file
#include "CATIData.h"
IID IID_CATIData = { 0x7db286f1, 0x218d, 0x0000,
    {0x02, 0x80, 0x02, 0x0a, 0x86, 0x00, 0x00, 0x00} };
CATImplementInterface(CATIData, CATBaseUnknown)

MyDataExtension的头文件如下:

#include "CATBaseUnknown.h"

class MyDataExtension : public CATBaseUnknown {
  CATDeclareClass;
  public :
    MyDataExtension();
    virtual ~MyDataExtension();
    virtual HRESULT __stdcall get_Length(int * oLength);
    virtual HRESULT __stdcall set_Length(int iLength);
  private :
    int _Length;
}

MyDataExtension的源文件如下:

#include "MyDataExtension.h"

CATImplementClass(MyDataExtension, // Extension class name
                  DataExtension,     // Data extension
                  CATBaseUnknown,    // Always OM-derive extensions from CATBaseUnknown
                  MyObject);         // Main class of the extended component

#include "TIE_CATIData.h"
TIE_CATIData(MyDataExtension);

MyDataExtension::MyDataExtension() {}
MyDataExtension::~MyDataExtension() {} 

HRESULT MyDataExtension::get_Length(int * oLength)
{ oLength = _Length; return S_OK; }

HRESULT MyDataExtension::set_Length(int iLength)
{ _Length = iLength; return S_OK; }

CATImplementClass宏声明类MyDataExtension是一个数据扩展,它扩展了MyObject类。第三个参数声明当前派生的组件只适用于组件主类,并且必须扩展时始终将其设置为CATBaseUnknown。

代码扩展

与数据扩展相比,只是没有数据成员。

共享扩展

即一个扩展类,扩展的主类不唯一。
扩展类的源文件代码如下:

CATBeginImplementClass(MyExtensionClassName,    // Begin declaration
                       DataExtension,
                       CATBaseUnknown,
                       TheFirstClassIExtend);
CATAddClassExtension(TheSecondClassIExtend);
CATAddClassExtension(TheThirdClassIExtend);
...
CATAddClassExtension(TheLastClassIExtend);
CATEndImplementClass(MyExtensionClassName);     // End declaration

若你想将一个已存在的扩展类变为某个新类的扩展,则在该新类的源文件中声明:

CATSupportImplementation(ExtensionClassName,
                         MyClassName,
                         ImplementedInterface);
                         

什么是TIE和BOA

我的理解是TIE是将接口和实现连接起来的一种技术,使得实现类不需要继承接口类。
在扩展类MyDataExtension的源文件中有如下代码:

CATImplementClass(CAAEDataExtension, // Extension class name
                  DataExtension,     // Data extension
                  CATBaseUnknown,    // Base component - Always OM-derive TIE extensions from CATBaseUnknown
                  MyObject);         // Implementation class of the extended component

#include "TIE_CATIData.h"
TIE_CATIData(CAAEDataExtension);

TIE是CAA推广的在运行时处理接口的技术。包含从TIE_CATIData生成的TIE_CATIData.h头文件。创建CATIData接口时创建的tsrc文件。这个文件包含TIE_CATIData宏代码.这个TIE_CATIData宏的调用实际上为TIE类创建了代码。宏参数是实现接口的类的名称。当组件被要求使用CATIData时,TIE类被实例化,QueryInterface返回一个指向它的指针。TIE是一个中间类,它在运行时连接客户机应用程序和组件,而在构建时没有任何链接。然而,在一些性能非常关键的场景中,实例化中间对象可能会花费很高。例如,如果一个组件被实例化数千次,并且针对每个组件实例请求指向给定接口的指针,那么就会创建数千个TIE对象,并可能导致内存分配问题。为了避免这种情况,CAA提出了另一种解决方案:BOA。

BOA是基本对象适配器。 BOA技术使QueryInterface返回一个指向实现接口的类的指针,而不是指向中间类的指针。 BOA在诸如上述方案的场景中保存每个组件的类实例。 即使返回指向实现类的指针,它也会作为接口指针返回,并且接口和实现之间没有比TIE更多的耦合。 客户端应用程序不知道哪个类实现了接口,与其头文件或模块没有构建时链接,因此只能调用此接口公开的方法。
BOA实现的代码头文件如下:

#include "CATIData.h"

class MyDataExtension : public CATIData {
  CATDeclareClass;
  public :
    MyDataExtension();
    virtual ~MyDataExtension();
    virtual HRESULT __stdcall get_Length(int * oLength);
    virtual HRESULT __stdcall set_Length(int iLength);
  private :
    int _Length;
};

源文件如下:

...
CATImplementClass(MyDataExtension, // Extension class name
                  DataExtension,     // Data extension
                  CATIData,          // Always OM-derive BOA extensions from the BOA implemented interface
                  MyObject);         // Implementation class of the extended component

CATImplementBOA(CATIData, MyDataExtension);
...

对比TIE实现,更改如下:
CATImplementClass的第三个参数必须设置为扩展类实现的接口
CATImplementBOA宏替换了TIE_CATIData宏。它的参数分别是BOA实现的接口和扩展类名(也是前述接口的实现类)。
注意,由于使用BOA实现类必须从接口派生,且CAA不支持多继承,因此一个给定的类只能实现一个接口,其他接口由TIE实现。因此,如果您的类实现了多个接口,请仔细选择要实现boa的适当接口。此外,BOA不能用于CodeExtension类型类。

创建组件实例

组件实例可由以下两种方法创建:
1、CATInstantiateComponent全局函数。使用这个组件工厂函数是推荐的方式,因为它不会在构建时将应用程序与组件共享库或DLL耦合,但必须由组件供应商启用。
2、用new操作符创建主类。
PS:根据后面看的文档,还有其他方式创建实例,如用CATCreateInstance和工厂创建。

使用CATInstantiateComponent

...
#include "CATInstantiateComponent.h"
...
  // Create a CATCmp instance and retrieving a pointer to IUnknown from CATCmp
  IUnknown * pIUnknownOnCATCmp = NULL;
  HRESULT rc = ::CATInstantiateComponent("CATCmp", IID_IUnknown, (void **) &pIUnknownOnCATCmp);
...

CATCmp是组件主类的名称。
IID_IUnknown是从CATCmp中获取指针的接口的IID。
pIUnknownOnCATCmp是检索到的指针。
在创建组件时,始终将其作为IUnknown或CATBaseUnknown指针处理。通常,CAA方法请求一个CATBaseUnknown指针,然后就可以获取指向此组件实现的任何接口的指针。
由于没有包含组件主类头文件的include语句,你的应用程序与提供组件的应用程序之间不存在构建时依赖关系。

使用new

...
#include "CATCmp.h"
...
  // Create a CATCmp instance
  IUnknown * pIUnknownOnCATCmp = NULL;
  pIUnkownOnCATCmp = (IUnknown *) new CATCmp();
...

在创建这样一个组件时,始终将其处理为IUnknown或CATBaseUnknown指针。通常,CAA方法请求一个CATBaseUnknown指针。然后,可以获取指向此组件实现的任何接口的指针。
这种方法将应用程序与在构建时提供组件的应用程序绑定在一起。这意味着,如果修改了后者,则必须重新构建应用程序。

QueryInterface

使用方法与V5相同,仅说明一下怎么比较以下两个接口指针是否一样。

比较两个接口指针

在任何情况下应用的惟一有效和安全的方法:从每个接口指针中,使用QueryInterface检索指向IUnknown的指针,或者指向CATBaseUnknown的指针,并比较这两个接口指针。对于指向给定组件实例的任何接口指针,如果有相同的IUnknown指针,或者相同的CATBaseUnknown指针,则组件实例是相同的。
代码如下:

...
// Assume we have two interface pointers pIXXOnCATCmp and pIYYOnCATCmp
// Create two pointers to IUnknown
  IUnknown * pIUnknownOnCATCmpFromIXX = NULL;
  IUnknown * pIUnknownOnCATCmpFromIYY = NULL;
// Retrieve the pointer to IUnknown from pIXXOnCATCmp
  HRESULT rc = pIXXOnCATCmp->QueryInterface(
                IID_IUnknown, (void **) & pIUnknownOnCATCmpFromIXX);
  
  if (SUCCEEDED(rc) && NULL != pIUnknownOnCATCmpFromIXX)
  {
    // Retrieve the pointer to IUnknown from pIYYOnCATCmp
    rc = pIYYOnCATCmp->QueryInterface(
                IID_IUnknown, (void **) & pIUnknownOnCATCmpFromIYY);
    if (SUCCEEDED(rc) && NULL != pIUnknownOnCATCmpFromIYY)
    {
      if (pIUnknownOnCATCmpFromIXX==pIUnknownOnCATCmpFromIYY)
      {
        // The underlying component instance is the same for both interface pointers
      }
      else
      {
        // Different component instances are pointed to
      }
    }
  }
...

智能指针

同V5相似,智能指针相当于在赋值是自带一个QueryInterface,可以不同类型的智能指针直接赋值。其他的,总的来说为了避免内存泄漏,需要合理的运用智能指针。在智能指针与普通指针转换时,如果缺少Release()可能会导致内存泄漏。因为智能指针内部自带一次AddRef(),在普通指针作为函数返回值或者函数传出参数时,函数内部也有一个AddRef(),而最后忘记Release(),只有智能指针自带的Release(),可能会导致少一次Release()从而导致内存泄漏。主要是在智能指针与普通指针混用时会出现问题
假设以下两个函数:

CATIXX     * ReturnAPointerToCATIXX();
CATIXX_var   ReturnASmartPointerToCATIXX();

不要像以下的方式用:

   ...
   CATIXX_var spIXXOnCATCmp = ::ReturnAPointerToCATIXX();
   ...                           // Use spIXXOnCATCmp
}    // spIXXOnCATCmp is automatically released when going out of scope

可以改为

   ...
   CATIXX_var spIXXOnCATCmp = ::ReturnAPointerToCATIXX();
   spIXXOnCATCmp->Release();    // Use spIXXOnCATCmp to release the returned interface pointer
   ...                           // Use spIXXOnCATCmp
}    // spIXXOnCATCmp is automatically released when going out of scope

也不要将智能指针赋值给普通指针:

...
CATIXX * pIXXOnCATCmp = spIXXCATCmp;
...
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值