You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将支持范围迭代的COM指针的迭代逻辑抽象为通用模板类

完全可以将迭代逻辑抽象为通用模板,你实现的两个版本核心逻辑高度一致,仅在「枚举器获取逻辑」和「枚举值转目标COM接口逻辑」两处存在差异,把这两部分抽为可定制逻辑即可。

通用模板基础实现

#include <atlbase.h>
#include <atlcom.h>

// 通用COM集合智能指针模板
// 模板参数说明:
// CollectionT: 集合接口类型,如IShellItemArray、IShellWindows
// ElementT: 迭代返回的元素接口类型,如IShellItem、IWebBrowser2
// EnumT: 枚举器接口类型,如IEnumShellItems、IEnumVARIANT
template <typename CollectionT, typename ElementT, typename EnumT>
class CComEnumCollectionPtr : public CComPtr<CollectionT>
{
public:
    using CComPtr<CollectionT>::CComPtr;

protected:
    // 子类实现:从集合接口获取枚举器实例
    virtual HRESULT GetEnum(CollectionT* pCollection, CComPtr<EnumT>& spEnum) const = 0;
    // 子类实现:从枚举器读取下一个元素,转换为目标接口类型
    virtual HRESULT ParseElement(EnumT* pEnum, CComPtr<ElementT>& spElement) const = 0;

private:
    class CIterator
    {
    public:
        CIterator(const CComEnumCollectionPtr* pOwner) : m_hr(S_FALSE), m_pOwner(pOwner)
        {
            CComPtr<EnumT> spEnum;
            if (m_pOwner && SUCCEEDED(m_pOwner->GetEnum(m_pOwner->p, spEnum)))
            {
                m_spEnum = spEnum;
                ++*this;
            }
        }

        const CIterator& operator++()
        {
            m_spElement.Release();
            if (m_spEnum)
            {
                m_hr = m_pOwner->ParseElement(m_spEnum, m_spElement);
            }
            return *this;
        }

        BOOL operator!=(HRESULT hr) const
        {
            return m_hr != hr;
        }

        ElementT* operator*() const
        {
            return m_spElement;
        }

    private:
        const CComEnumCollectionPtr* m_pOwner = nullptr;
        CComPtr<EnumT> m_spEnum;
        CComPtr<ElementT> m_spElement;
        HRESULT m_hr = S_FALSE;
    };

public:
    CIterator begin() const
    {
        return CIterator(this);
    }

    HRESULT end() const
    {
        return S_FALSE;
    }
};

针对具体集合类型的实现

只需要继承通用模板,实现两个虚方法即可,不需要重复写迭代逻辑。

IShellItemArray 实现

class CShellItemArrayPtr : public CComEnumCollectionPtr<IShellItemArray, IShellItem, IEnumShellItems>
{
public:
    using Base = CComEnumCollectionPtr<IShellItemArray, IShellItem, IEnumShellItems>;
    using Base::CComEnumCollectionPtr;

protected:
    HRESULT GetEnum(IShellItemArray* pCollection, CComPtr<IEnumShellItems>& spEnum) const override
    {
        return pCollection->EnumItems(&spEnum);
    }

    HRESULT ParseElement(IEnumShellItems* pEnum, CComPtr<IShellItem>& spElement) const override
    {
        return pEnum->Next(1, &spElement, nullptr);
    }
};

IShellWindows 实现

class CShellWindowsPtr : public CComEnumCollectionPtr<IShellWindows, IWebBrowser2, IEnumVARIANT>
{
public:
    using Base = CComEnumCollectionPtr<IShellWindows, IWebBrowser2, IEnumVARIANT>;
    using Base::CComEnumCollectionPtr;

protected:
    HRESULT GetEnum(IShellWindows* pCollection, CComPtr<IEnumVARIANT>& spEnum) const override
    {
        CComPtr<IUnknown> spUnk;
        HRESULT hr = pCollection->_NewEnum(&spUnk);
        if (SUCCEEDED(hr))
        {
            hr = spUnk->QueryInterface(&spEnum);
        }
        return hr;
    }

    HRESULT ParseElement(IEnumVARIANT* pEnum, CComPtr<IWebBrowser2>& spElement) const override
    {
        CComVariant var;
        HRESULT hr = pEnum->Next(1, &var, nullptr);
        if (hr == S_OK && var.vt == VT_DISPATCH && var.pdispVal)
        {
            hr = var.pdispVal->QueryInterface(&spElement);
        }
        return hr;
    }
};

使用方式

和你之前的用法完全一致,直接用范围for遍历即可:

CShellItemArrayPtr spArray;
// 初始化spArray的逻辑省略
for (const auto psi : spArray)
{
    // 处理每一个IShellItem对象
}

如果对性能要求极高,不想承担虚函数的微小开销,也可以把GetEnum和ParseElement改为模板策略类参数,编译期绑定逻辑,运行期零开销。


内容的提问来源于stack exchange,提问作者user15284017

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 21:30:03