COM编程之四引⽤计数
【1】客户为什么不应直接控制组件的⽣命期?
假设⼀个组件A正在使⽤另⼀个组件B,可想组件A(客户)代码中肯定有若⼲个指向组件B接⼝的指针。
那么这种情况下,当使⽤完⼀个接⼝⽽仍然在使⽤另⼀个接⼝时,是不能将组件释放掉的。
⽽且很难知道两个接⼝指针是否指向同⼀组件,因此决定何时可以安全的释放⼀个组件将是极为困难的。
得知两个接⼝指针是否是指向同⼀对象的唯⼀⽅法是查询这两个接⼝的IUnknown接⼝指针,然后对两者结果进⾏⽐较。当程序越来越复杂时,决定何时可以释放⼀个组件是极为复杂的事情。
解决这个技术问题的办法:我们可以通知组件何时需要使⽤它的某个接⼝以及何时使⽤完接⼝,⽽不是直接将接⼝删除。对组件的释放也应该由组件在客户使⽤完其各个接⼝之后⾃⼰完成。
IUnknown的两个成员函数AddRef和Release的作⽤就是给客户提供⼀种让它指⽰何时处理完⼀个接⼝的⼿段。
【2】引⽤计数简介
AddRef和Release实现的是⼀种名为引⽤计数的内存管理计数。
引⽤计数是使组件能够⾃⼰将⾃⼰删除的最简单同时也是效率最⾼的⽅法。
COM组件将维护⼀个称作是引⽤计数的数值。
当客户从组件取得⼀个接⼝时,此引⽤计数将增1。
当客户使⽤完某个接⼝后,组件的引⽤计数将减1。
当引⽤计数值为0时,组件即可将⾃⼰从内存中删除。
当创建某个已有接⼝的另外⼀个引⽤时,客户也将会增⼤相应组件的引⽤计数值。
【3】正确地使⽤引⽤计数,遵循的规则
(1)在返回之前调⽤AddRef。
对于那些返回接⼝指针的函数,在返回之前应⽤相应的指针调⽤AddRef。
这些函数包括QueryInterface及CreateInstance。
这样当客户从这种函数得到⼀个接⼝后,它将⽆需调⽤AddRef。
(2)使⽤完接⼝调⽤Release。
在使⽤完某个接⼝之后应调⽤此接⼝的Release函数。
以上两条代码如下:
1//Create a new component
2 IUnknown* pLUnknown = CreateInstance();
3//Get interface IX
4 IX* pIX = NULL;
5 HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
6if (SUCCEEDED(hr))
7 {
8    pIX->fx();  //Use Interface IX
9    pIX->Release();
10 }
11 pLUnknown->Release();
(3)在赋值之后调⽤AddRef。
在将⼀个接⼝指针赋给另外⼀个接⼝指针时,应调⽤AddRef。
换句话说,在建⽴接⼝的另外⼀个应⽤之后应增加相应组件的应⽤计数。
代码如下:
1//Create a new component
2 IUnknown* pLUnknown = CreateInstance();
3//Get interface IX
4 IX* pIX = NULL;
5 HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX);
6if (SUCCEEDED(hr))
7 {
8    pIX->fx();  //Use Interface IX
9    IX* pIX2 = pIX; //Make a copy of pIX
10
11    pIX2->AddRef();
12    pIX2->fx();  //Do  something
13    pIX2->Release();
14
15    pIX->Release();
16 }
【4】引⽤计数的完整⽰例
代码如下:
1//
2// RefCount.cpp
3// To compile, use: cl RefCount.cpp UUID.lib
4//
5 #include <iostream>
6using namespace std;
7 #include <objbase.h>
8
9void trace(const char* msg) { cout << msg << endl ;}
10
11// Forward references for GUIDs
12extern const IID IID_IX ;
13extern const IID IID_IY ;
14extern const IID IID_IZ ;
15
16// Interfaces
17interface IX : IUnknown
18 {
19virtual void __stdcall Fx() = 0 ;
20 } ;
21
22interface IY : IUnknown
23 {
24virtual void __stdcall Fy() = 0 ;
25 } ;
26
27interface IZ : IUnknown
28 {
29virtual void __stdcall Fz() = 0 ;
30 } ;
31
32
33//
34// Component
35//
36class CA : public IX, public IY
37 {
38// IUnknown implementation
39virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ;            40virtual ULONG __stdcall AddRef() ;
41virtual ULONG __stdcall Release() ;
42
43// Interface IX implementation
44virtual void __stdcall Fx() { cout << "Fx" << endl ;}
45
46// Interface IY implementation
47virtual void __stdcall Fy() { cout << "Fy" << endl ;}
48
49public:
50// Constructor
51    CA() : m_cRef(0) {}
52
字符串常量的使用
53// Destructor
54    ~CA() { trace("CA:    Destroy self.") ;}
55
56private:
57long m_cRef;
58 } ;
59
60 HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
61 {
62if (iid == IID_IUnknown)
63    {
64        trace("CA QI:  Return pointer to IUnknown.") ;
65        *ppv = static_cast<IX*>(this) ;
66    }
67else if (iid == IID_IX)
68    {
69        trace("CA QI:  Return pointer to IX.") ;
70        *ppv = static_cast<IX*>(this) ;
71    }
72else if (iid == IID_IY)
73    {
74        trace("CA QI:  Return pointer to IY.") ;
75        *ppv = static_cast<IY*>(this) ;
76    }
77else
78    {
79        trace("CA QI:  Interface not supported.") ;
80        *ppv = NULL ;
81return E_NOINTERFACE;
82    }
83    reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
84return S_OK ;
85 }
86
87 ULONG __stdcall CA::AddRef()
88 {
89    cout << "CA:    AddRef = " << m_cRef+1 << '.' << endl ;
90return InterlockedIncrement(&m_cRef) ;
91 }
92
93 ULONG __stdcall CA::Release()
94 {
95    cout << "CA:    Release = " << m_cRef-1 << '.' << endl ;
96
97if (InterlockedDecrement(&m_cRef) == 0)
98    {
99        delete this ;
100return0 ;
101    }
102return m_cRef ;
103 }
104
105//
106// Creation function
107//
108 IUnknown* CreateInstance()
109 {
110    IUnknown* pI = static_cast<IX*>(new CA) ;
111    pI->AddRef() ;
112return pI ;
113 }
114
115//
116// IIDs
117//
118// {32bb8320-b41b-11cf-a6bb-0080c7b2d682}
119static const IID IID_IX =
120 {0x32bb8320, 0xb41b, 0x11cf,
121 {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
122
123// {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
124static const IID IID_IY =
125 {0x32bb8321, 0xb41b, 0x11cf,
126 {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
127
128// {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
129static const IID IID_IZ =
130 {0x32bb8322, 0xb41b, 0x11cf,
131 {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
132
133//
134// Client
135//
136int main()
137 {
138    HRESULT hr ;
139
140    trace("Client: Get an IUnknown pointer.") ;
141    IUnknown* pIUnknown = CreateInstance() ;
142
143
144    trace("Client: Get interface IX.") ;
145
146    IX* pIX = NULL ;
147    hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX) ; 148
149if (SUCCEEDED(hr))
150    {
151        trace("Client: Succeeded getting IX.") ;
152        pIX->Fx() ;          // Use interface IX.
153        pIX->Release() ;
154    }
155
156
157    trace("Client: Get interface IY.") ;
158
159    IY* pIY = NULL ;
160    hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY) ; 161if (SUCCEEDED(hr))
162    {
163        trace("Client: Succeeded getting IY.") ;
164        pIY->Fy() ;          // Use interface IY.
165        pIY->Release() ;
166    }
167
168
169    trace("Client: Ask for an unsupported interface.") ;
170
171    IZ* pIZ = NULL ;
172    hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ) ; 173if (SUCCEEDED(hr))
174    {
175        trace("Client: Succeeded in getting interface IZ.") ; 176        pIZ->Fz() ;
177        pIZ->Release() ;
178    }
179else
180    {
181        trace("Client: Could not get interface IZ.") ;
182    }
183
184
185    trace("Client: Release IUnknown interface.") ;
186    pIUnknown->Release() ;
187
188return0;
189 }
190//Output
191/*
192Client: Get an IUnknown pointer.
193CA:    AddRef = 1.
194Client: Get interface IX.
195CA QI:  Return pointer to IX.
196CA:    AddRef = 2.
197Client: Succeeded getting IX.
198Fx
199CA:    Release = 1.
200Client: Get interface IY.
201CA QI:  Return pointer to IY.
202CA:    AddRef = 2.
203Client: Succeeded getting IY.
204Fy
205CA:    Release = 1.
206Client: Ask for an unsupported interface.
207CA QI:  Interface not supported.
208Client: Could not get interface IZ.
209Client: Release IUnknown interface.
210CA:    Release = 0.
211CA:    Destroy self.
212*/
【5】引⽤计数的优化
正确使⽤引⽤计数规则(3)的⽰例代码做优化如下:
1//Create a new component
2 IUnknown* pLUnknown = CreateInstance();
3//Get interface IX
4 IX* pIX = NULL;
5 HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX); 6if (SUCCEEDED(hr))
7 {
8    pIX->fx();  //Use Interface IX
9    IX* pIX2 = pIX; //Make a copy of pIX
10
11//    pIX2->AddRef();  //unnecessary
12    pIX2->fx();  //Do  something
13//  pIX2->Release();    //unnecessary
14
15    pIX->Release();
16 }
关键是出那些⽣命期嵌套在引⽤同⼀接⼝指针⽣命期内的接⼝指针。 1void Fun(IX* pIx)
2 {
3    pIx->Fx();
4 }
5
6void main()
7 {
8    IUnknown* pLUnknown = CreateInstance();
9//Get interface IX
10    IX* pIX = NULL;
11    HRESULT hr = pLUnknown->QueryInterface(IID_IX, (void**)&pIX); 12if (SUCCEEDED(hr))
13    {
14        Fun(pIX);
15        pIX->Release();
16    }
17 }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。