COM中集合和枚举器笔记(1)概念

时间:2022-09-03 16:08:59

 

参考:

http://www.cnblogs.com/clingingboy/archive/2011/06/15/2081786.html

一.概念

如同标准库一般,也有容器(即集合类)和迭代器

void main() {
    // Populate the collection vector<long> rgPrimes;
    for (long n = 0; n != 1000; ++n) {
        if (IsPrime(n)) rgPrimes.push_back(n);
    }

    // Count the number of items in the collection cout << "Primes: " << rgPrimes.size() << endl;

    // Iterate over the collection using sequential access vector<long>::iterator begin = rgPrimes.begin();
    vector<long>::iterator end = rgPrimes.end();
    for (vector<long>::iterator it = begin; it != end; ++it) {
        cout << *it << " ";
    }
    cout << endl;
}

二.COM中集合和枚举器的替代品

ICollection提供基本的2个方法

Count取得元素数量,Item通过索引获取一个元素.

除此之外_NewEnum将获取一个与集合相关的枚举器.引用一段话

通过ICollection操纵数组大体上有两种方法。一种是通过Item属性用数组下标取得元素。这种方式,每次只能取得一个元素,而且要传递下标对象,所以效率比较低下。另一种方法是通过枚举器。数组对象的枚举器通过_NewEnum属性取得。通过枚举器只能按顺序获取元素,但每次可以取得任意多的元素,所以效率较高。ICollection对象可以只实现其中的一种访问方法,也可以两种都实现。ICollection中还有一个重要属性:Count。Count属性返回数组的长度,对于无法确定长度的数组,也可以不实现Count属性。

 

[ object, dual ]
template <typename T>
interface ICollection : IDispatch {
    [propget]
    HRESULT Count([out, retval] long* pnCount);

    [id(DISPID_VALUE), propget]
    HRESULT Item([in] long n, [out, retval] T* pnItem);

    [id(DISPID_NEWENUM), propget]
    HRESULT _NewEnum([out, retval] IUnknown** ppEnum);
};

template <typename T>
interface IEnum : IUnknown {
    [local]
    HRESULT Next([in] ULONG celt,
        [out] T* rgelt,
        [out] ULONG *pceltFetched);

    [call_as(Next)] // Discussed later... HRESULT RemoteNext([in] ULONG celt,
        [out, size_is(celt),
        length_is(*pceltFetched)] T* rgelt,
        [out] ULONG *pceltFetched);

    HRESULT Skip([in] ULONG celt);
    HRESULT Reset();
    HRESULT Clone([out] IEnum<T> **ppenum);
}

三.集合和枚举器的使用方法

通过一个例子来了解集合和枚举器的使用方法

自定义集合和枚举器接口(暂时不关注实现)

[dual]
interface IPrimeNumbers : IDispatch {
    HRESULT CalcPrimes([in] long min, [in] long max);

    [propget]
    HRESULT Count([out, retval] long* pnCount);

    [propget, id(DISPID_VALUE)]
    HRESULT Item([in] long n, [out, retval] long* pnPrime);

    [propget, id(DISPID_NEWENUM)] // Not quite right... HRESULT _NewEnum([out, retval] IEnumPrimes** ppEnumPrimes);
};

interface IEnumPrimes : IUnknown {
    [local]
    HRESULT Next([in] ULONG celt,
        [out] long* rgelt,
        [out] ULONG *pceltFetched);

    [call_as(Next)]
    HRESULT RemoteNext([in] ULONG celt,
        [out, size_is(celt),
        length_is(*pceltFetched)] long* rgelt,
        [out] ULONG *pceltFetched);

    HRESULT Skip([in] ULONG celt);
    HRESULT Reset();
    HRESULT Clone([out] IEnumPrimes **ppenum);
};

调用示例

void main() {
    CoInitialize(0);

    CComPtr<IPrimeNumbers> spPrimes;
    if (SUCCEEDED(spPrimes.CoCreateInstance(CLSID_PrimeNumbers))) {
        // Populate the collection HRESULT hr = spPrimes->CalcPrimes(0, 1000);

        // Count the number of items in the collection long nPrimes;
        hr = spPrimes->get_Count(&nPrimes);
        cout << "Primes: " << nPrimes << endl;

        // Enumerate over the collection using sequential access CComPtr<IEnumPrimes> spEnum;
        hr = spPrimes->get__NewEnum(&spEnum);

        const size_t PRIMES_CHUNK = 64;
        long rgnPrimes[PRIMES_CHUNK];

        do {
            ULONG celtFetched;
            hr = spEnum->Next(PRIMES_CHUNK, rgnPrimes, &celtFetched);
            if (SUCCEEDED(hr)) {
                if (hr == S_OK) celtFetched = PRIMES_CHUNK;
                for (long* pn = &rgnPrimes[0];
                    pn != &rgnPrimes[celtFetched]; ++pn) {
                        cout << *pn << " ";
                }
            }
        }
        while (hr == S_OK);
        cout << endl;

        spPrimes.Release();
    }

    CoUninitialize();
}

关注Next方法

HRESULT Next([in] ULONG celt,
    [out] long* rgelt,
    [out] ULONG *pceltFetched);

 

celt表示要取的元素个数,rgelt用于存储取到的数据指针,pceltFetched表示实际取到的元素个数

理解这个方式非常关键,由于rgelt存储的指针是外部存储,那么Next方法内部就需要一个的策略来填充这个外部数据的指针,这就从而引出了拷贝策略这个话题,这个到后面实现的时候提到