代码改变世界

ATL 多步构造

2011-06-14 22:31  Clingingboy  阅读(2387)  评论(0编辑  收藏  举报

 

一.FinalConstruct && FinalRelease

显然构造函数调用虚方法是有问题的

class Base {
public:
    Base() { Init(); }
    virtual void Init() {}
};

class Derived : public Base {
public:
    virtual void Init() {}
};

分部构造的意思就是在构造函数完全初始化好之后,然后再调用初始化的一个函数,

FinalConstruct 就是这么一个函数(我只能先这么理解了,为什么是叫Init之类的方法呢) 另外一个原因是传递错误信息。
同理FinalRelease的代码本来是放在析构函数里面的,析构函数调用虚方法是同样的道理,在FinalRelease调用虚方法(在调用虚构函数之前) FinalConstruct 和FinalRelease构建了一个对象创建和销毁的一个生命周期(但不在构造函数和析构函数里面)

二.FinalConstruct 防止过早析构

即在调用方法时,可能获取其他接口,以导致减少引用,过早析构

// CPenguin implementation
HRESULT CPenguin::FinalConstruct() {
    HRESULT hr;
    hr = CoCreateInstance(CLSID_EarthAtmosphere, 0, CLSCTX_ALL,
        IID_IAir, (void**)&m_pAir);
    if( SUCCEEDED(hr) ) {
        // Pass reference to object with reference count of 0
        hr = m_pAir->CheckSuitability(GetUnknown());
    }
    return hr;
}

// CEarthAtmosphere implementation in separate server
STDMETHODIMP CEarthAtmosphere::CheckSuitability(IUnknown* punk) {
    IBreatheO2* pbo2 = 0;
    HRESULT hr = E_FAIL;

    // CPenguin's lifetime increased to 1 via QI
    hr = punk->QueryInterface(IID_IBreatheO2, (void**)&pbo2);
    if( SUCCEEDED(hr) ) {
        pbo2->Release(); // During this call, lifetime decreases
        // to 0 and destruction sequence begins...
    }

    return (SUCCEEDED(hr) ? S_OK : E_FAIL);
}

三.手动添加引用计数防止析构

STDMETHODIMP
    CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
    void** ppv) {
        *ppv = 0;
        if( !pUnkOuter ) {
            CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
            if( FAILED(hr) ) return E_OUTOFMEMORY;

            // Protect object from pre-mature destruction
            pobj->InternalAddRef();
            hr = pobj->FinalConstruct();
            pobj->InternalRelease();

            if( SUCCEEDED(hr) ) ...
                return hr;
        }
        ...
}

四.恰当好处的引用计数

注意第二点并非绝对,是说有可能性,那么第三部就是非必要的,这两个方法定义在基类,默认为为空方法,必要时可以重载

STDMETHODIMP
    CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,
    void** ppv) {
        *ppv = 0;
        if( !pUnkOuter ) {
            CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
            if( FAILED(hr) ) return E_OUTOFMEMORY;

            // Protect object from pre-mature destruction (maybe)
            pobj->InternalFinalConstructAddRef();
            hr = pobj->FinalConstruct();
            pobj->InternalFinalConstructRelease();

            if( SUCCEEDED(hr) ) ...
                return hr;
        }
        ...
}

五.使用DECLARE_PROTECT_FINAL_CONSTRUCT宏

为重写提供便利

#define DECLARE_PROTECT_FINAL_CONSTRUCT()\
    void InternalFinalConstructAddRef() {InternalAddRef();}\
    void InternalFinalConstructRelease() {InternalRelease();}

六.关于ATL_NO_VTABLE

参考这里吧:http://www.cnblogs.com/weiqubo/archive/2011/03/16/1985773.html

七.ATL框架的_AtlInitialConstruct和_AtlFinalRelease

目的相同(错误处理),但用于ATL框架的的生命周期,其初始化早于FinalConstruct