COM组件弱引用的简单实现(C++)
COM组件弱引用的简单实现
The simple implementation of the weak reference of the COM object.
说明:我们知道boost用shared_ptr,weak_ptr实现了指针的智能化管理,使用它们可以防止C++常见的内存泄露问题。COM组件的管理和指针类似却又不同,COM组件同样需要在使用的时候调用AddRef和Release来管理组件的引用计数,为了方便管理,ATL库提供了CComPtr等智能组件指针来简化COM组件的使用。可是我的问题是如何去观察一个com组件而不影响它的引用计数,并能在合适的时候将观察对象转换成真实的COM引用。
从COM组件的实现原理,我们可以知道COM组件的弱引用并不好实现。原因在于COM组件的引用计数是包含在COM对象中的,如此,当COM对象被销毁时,也就无法通过获取它的引用计数值来判断该COM对象是否存在了,那么弱引用也就无法判断何时可以将弱引用转换成强引用了。
那么boost的weak_ptr是如何实现的呢?很简单,boost中的shared_ptr,weak_ptr将引用计数和对象本身分离开,这样就算对象本身被销毁了,只要还有弱引用仍在观察该对象,那么弱引用都可以通过判断引用计数中的值来判断对象是否销毁,这样就可以在对象已经销毁后返回失败的强引用即可。
那么COM组件可以借鉴boost的这种实现方式。但是有几个问题:
1.引入额外的引用计数对象后,如何处理内部引用计数和外部引用计数的一致性。
2.由于需要保证内部引用计数和外部引用计数的一致性,线程安全就成了一个问题,因为需要保证对两个计数变量同时增减,单一变量可以用原子增减,多个变量似乎只能用锁来解决了。Linux下面用pthread_mutex_t来实现锁,windows下可以用关键代码段。
我把问题简化了,因此我只是简单实现了类似boost的CComSharedPtr,CComWeakPtr,对于上述两个问题,首先 CcomWeakPtr只能观察由CComSharedPtr封装的COM组件,因此,如果使用了CComPtr来引用一个组件,那么CComWeakPtr是观察不到的,也就是此时即使CComPtr还有效,如果CComSharedPtr管理的组件引用全部失效,那么CComWeakPtr仍然会返回失败。第二,CComSharedPtr中的计数只包含由CComSharedPtr管理的对象个数,而不一定是实际COM组件对象被引用的个数。这样,COM组件对象的引用就包括两个部分:CComSharedPtr管理的和其他指针直接管理的。
因此CComSharedPtr的使用需要注意:若是整个项目的COM对象全部由CComSharedPtr封装管理,那么CComSharedPtr和 CcomWeakPtr会工作的很好。如果项目代码中还有CComPtr等其他指针对COM进行引用的话,那么CComSharedPtr一切正常使用,但是CComWeakPtr在将COM弱引用转换成强引用时,即使失败了,也不代表COM对象被销毁了,只能代表已经没有对应的CComSharedPtr管理的COM引用了。
最后,该实现不是线程安全的,如果需要的话,可以修改代码的增减引用计数部分(具体修改可以参见代码注释的pthread_mutex_t部分,windows下可以用关键代码段)。另外,代码草率完成,定有不适之处,有什么问题可以留言告知。
PS:COM组件的弱引用的需求在实际项目中可能是设计不当的结果,在此实现完全是为了熟悉弱引用的实现原理。
源码:WeakCom
以下是代码实现:
借鉴了boost的大量源码:
1 #include <stdio.h>
2 #include <memory>
3 #include <boost/smart_ptr.hpp>
4 #include <pthread.h>
5 #include <unistd.h>
6 using namespace std;
7 //这个基类就是为了在com组件之外添加额外的引用计数对象,这个是因为当com对象被销毁时无法
8 //访问com内部的引用来探测com对象是否存在,而通过引入额外的引用计数对象,我们可以通过
9 //使用该com对象的弱引用来探测com对象是否存在,直到所有观察该com对象的弱引用全部销毁后,
10 //额外的引用计数对象才会销毁自身。这样就达到了观察com对象却不影响com对象引用计数的目的。
11 //缺点:需要额外维护两个引用计数的一致性,因此无法做到线程安全。而且只能保证维护
12 //CComSharedPtr所管理的com对象,其他如CComPtr不在管理范围内,因此相应的弱引用也只能观察
13 //由CComSharedPtr所管理的com对象,当所有的CComSharedPtr管理的com对象销毁后,弱引用返回
14 class com_counted_base
15 {
16 private:
17
18 com_counted_base( com_counted_base const & );
19 com_counted_base & operator= ( com_counted_base const & );
20
21 long use_count_; // #shared
22 long weak_count_; // #weak + (#shared != 0)
23
24 // pthread_mutex_t refmutex;
25 public:
26
27 com_counted_base(): use_count_( 1 ), weak_count_( 1 )
28 {
29 // pthread_mutex_init(&refmutex,NULL);
30 }
31
32 virtual ~com_counted_base() // nothrow
33 {
34 // pthread_mutex_destroy(&refmutex);
35 }
36
37 // dispose() is called when use_count_ drops to zero, to release
38 // the resources managed by *this.
39
40 virtual void dispose() = 0; // nothrow
41 virtual ULONG com_add_ref() = 0;
42 virtual ULONG com_release() = 0;
43
44 // destroy() is called when weak_count_ drops to zero.
45
46 virtual void destroy() // nothrow
47 {
48 delete this;
49 }
50
51 void add_ref_copy()
52 {
53 // pthread_mutex_lock(&refmutex);
54 ++use_count_;
55 //如果需要测试线程安全,可以在此加入sleep
56 // usleep(1);
57 com_add_ref();
58 // pthread_mutex_unlock(&refmutex);
59 }
60
61 bool add_ref_lock() // true on success
62 {
63
64 if( use_count_ == 0 ) return false;
65 add_ref_copy();
66 return true;
67 }
68
69 void release() // nothrow
70 {
71 // pthread_mutex_lock(&refmutex);
72 com_release();
73 //如果需要测试线程安全,可以在此加入sleep
74 // usleep(1);
75 if( --use_count_ == 0 )
76 {
77 dispose();
78 weak_release();
79 }
80 // pthread_mutex_unlock(&refmutex);
81 }
82 void weak_add_ref() // nothrow
83 {
84 ++weak_count_;
85 }
86
87 void weak_release() // nothrow
88 {
89 if( --weak_count_ == 0 )
90 {
91 destroy();
92 }
93 }
94
95 long use_count() const // nothrow
96 {
97 return static_cast<long const volatile &>( use_count_ );
98 }
99 };
100 template <typename T> class com_counted_impl: public com_counted_base
101 {
102 private:
103 T * px_;
104
105 com_counted_impl( com_counted_impl const & );
106 com_counted_impl & operator= ( com_counted_impl const & );
107
108 typedef com_counted_impl this_type;
109
110 public:
111 explicit com_counted_impl( T * px, bool isaddrefneeded): px_( px )
112 {
113 if(px_!=NULL && isaddrefneeded)
114 com_add_ref();
115 }
116 virtual ULONG com_add_ref()
117 {
118 return px_->AddRef();
119 }
120 virtual ULONG com_release()
121 {
122 return px_->Release();
123 }
124 virtual void dispose() // nothrow
125 {
126 //普通数据指针这里需要delete自己,但是com组件指针不用,因为当com组件引用降为0时会删除自己,和普通数据指针不同
127 }
128 };
129 class com_weak_count;
130
131 class com_shared_count
132 {
133 private:
134
135 com_counted_base* pi_;
136
137 friend class com_weak_count;
138
139 public:
140
141 com_shared_count(): pi_(0) // nothrow
142 {
143 }
144
145 template <typename Y> explicit com_shared_count( Y * p, bool isaddrefneeded = true ): pi_( 0 )
146 {
147 pi_ = new com_counted_impl<Y>( p , isaddrefneeded);
148
149 if( pi_ == 0 )
150 {
151 p->Release();
152 boost::throw_exception( std::bad_alloc() );
153 }
154 }
155
156 ~com_shared_count() // nothrow
157 {
158 if( pi_ != 0 ) pi_->release();
159 }
160
161 com_shared_count(com_shared_count const & r): pi_(r.pi_) // nothrow
162 {
163 if( pi_ != 0 ) pi_->add_ref_copy();
164 }
165
166 explicit com_shared_count(com_weak_count const & r); // throws bad_weak_ptr when r.use_count() == 0
167
168 com_shared_count & operator= (com_shared_count const & r) // nothrow
169 {
170 com_counted_base * tmp = r.pi_;
171
172 if( tmp != pi_ )
173 {
174 if( tmp != 0 ) tmp->add_ref_copy();
175 if( pi_ != 0 ) pi_->release();
176 pi_ = tmp;
177 }
178 return *this;
179 }
180 void swap(com_shared_count & r) // nothrow
181 {
182 com_counted_base * tmp = r.pi_;
183 r.pi_ = pi_;
184 pi_ = tmp;
185 }
186
187 long use_count() const // nothrow
188 {
189 return pi_ != 0? pi_->use_count(): 0;
190 }
191
192 bool unique() const // nothrow
193 {
194 return use_count() == 1;
195 }
196
197 friend inline bool operator==(com_shared_count const & a, com_shared_count const & b)
198 {
199 return a.pi_ == b.pi_;
200 }
201
202 };
203
204 class com_weak_count
205 {
206 private:
207
208 com_counted_base * pi_;
209 friend class com_shared_count;
210
211 public:
212
213 com_weak_count(): pi_(0) // nothrow
214 {
215 }
216
217 com_weak_count(com_shared_count const & r): pi_(r.pi_) // nothrow
218 {
219 if(pi_ != 0) pi_->weak_add_ref();
220 }
221
222 com_weak_count(com_weak_count const & r): pi_(r.pi_) // nothrow
223 {
224 if(pi_ != 0) pi_->weak_add_ref();
225 }
226
227 ~com_weak_count() // nothrow
228 {
229 if(pi_ != 0) pi_->weak_release();
230 }
231
232 com_weak_count & operator= (com_shared_count const & r) // nothrow
233 {
234 com_counted_base * tmp = r.pi_;
235 if(tmp != 0) tmp->weak_add_ref();
236 if(pi_ != 0) pi_->weak_release();
237 pi_ = tmp;
238
239 return *this;
240 }
241
242 com_weak_count & operator= (com_weak_count const & r) // nothrow
243 {
244 com_counted_base * tmp = r.pi_;
245 if(tmp != 0) tmp->weak_add_ref();
246 if(pi_ != 0) pi_->weak_release();
247 pi_ = tmp;
248
249 return *this;
250 }
251
252 void swap(com_weak_count & r) // nothrow
253 {
254 com_counted_base * tmp = r.pi_;
255 r.pi_ = pi_;
256 pi_ = tmp;
257 }
258
259 long use_count() const // nothrow
260 {
261 return pi_ != 0? pi_->use_count(): 0;
262 }
263
264 friend inline bool operator==(com_weak_count const & a, com_weak_count const & b)
265 {
266 return a.pi_ == b.pi_;
267 }
268 };
269 inline com_shared_count::com_shared_count( com_weak_count const & r ): pi_( r.pi_ )
270 {
271 if( pi_ == 0 || !pi_->add_ref_lock() )
272 {
273 boost::throw_exception( boost::bad_weak_ptr() );
274 }
275 }
276 template <typename T> class CComWeakPtr;
277 template <typename T> class CComSharedPtr
278 {
279 private:
280 T* px;
281 com_shared_count pn;
282 typedef CComSharedPtr<T> this_type;
283 public:
284
285 CComSharedPtr(): px(0), pn() // never throws in 1.30+
286 {
287 }
288 ~CComSharedPtr()
289 {
290
291 }
292 explicit CComSharedPtr( T* p ): px( p ), pn( p )
293 {
294 }
295 // generated copy constructor, assignment, destructor are fine...
296
297 template <typename Y> CComSharedPtr & operator=(CComSharedPtr<Y> const & r) // never throws
298 {
299 px = r.px;
300 pn = r.pn; // shared_count::op= doesn't throw
301 return *this;
302 }
303 template <typename Y> explicit CComSharedPtr(CComWeakPtr<Y> const & r): pn(r.pn) // may throw
304 {
305 // it is now safe to copy r.px, as pn(r.pn) did not throw
306 px = r.px;
307 }
308
309 template<typename Y> CComSharedPtr(CComSharedPtr<Y> const & r): px(r.px), pn(r.pn)
310 {
311
312 }
313 template<class Y>
314 CComSharedPtr(CComSharedPtr<Y> const & r, boost::detail::dynamic_cast_tag): px(dynamic_cast<T *>(r.px)), pn(r.pn)
315 {
316 if(px == 0) // need to allocate new counter -- the cast failed
317 {
318 pn = com_shared_count();
319 }
320 }
321
322 operator T*() const
323 {
324 return px;
325 }
326
327 //使用该操作符返回的指针被修改指向新的com组件后,必须调用init_count函数进行计数初始化
328 T** operator&() throw()
329 {
330 // BOOST_ASSERT(px==NULL);
331 return &px;
332 }
333
334 void init_com_count()
335 {
336 //初始化通过获取原始数据指针直接修改的com引用,由于直接修改原始com引用,跳过了
337 //额外引用计数的初始化,因此需要调用该函数进行额外计数初始化。
338 //由于直接修改的com引用必然是已经调用过AddRef的,因此传递false以表示不再需要AddRef了。
339 com_shared_count temp(px,false);
340 pn = temp;
341 }
342
343 void reset() // never throws in 1.30+
344 {
345 this_type().swap(*this);
346 }
347
348 void reset(T * p) // Y must be complete
349 {
350 BOOST_ASSERT(p == 0 || p != px); // catch self-reset errors
351 this_type(p).swap(*this);
352 }
353 T& operator* () const // never throws
354 {
355 BOOST_ASSERT(px != 0);
356 return *px;
357 }
358
359 T * operator-> () const // never throws
360 {
361 BOOST_ASSERT(px != 0);
362 return px;
363 }
364
365 T * get() const // never throws
366 {
367 return px;
368 }
369 // implicit conversion to "bool"
370 operator bool () const
371 {
372 return px != 0;
373 }
374 // operator! is redundant, but some compilers need it
375 bool operator! () const // never throws
376 {
377 return px == 0;
378 }
379
380 bool unique() const // never throws
381 {
382 return pn.unique();
383 }
384
385 long use_count() const // never throws
386 {
387 return pn.use_count();
388 }
389 void swap(CComSharedPtr<T>& other)
390 {
391 std::swap(px, other.px);
392 pn.swap(other.pn);
393 }
394 private:
395 template<class Y> friend class CComSharedPtr;
396 template<class Y> friend class CComWeakPtr;
397 };
398 template<class T, class U> CComSharedPtr<T> dynamic_pointer_cast(CComSharedPtr<U> const & r)
399 {
400 return CComSharedPtr<T>(r, boost::detail::dynamic_cast_tag());
401 }
402 template <typename T> class CComWeakPtr
403 {
404 private:
405
406 typedef CComWeakPtr<T> this_type;
407
408 public:
409
410 CComWeakPtr(): px(0), pn() // never throws in 1.30+
411 {
412 }
413
414 // generated copy constructor, assignment, destructor are fine
415
416
417 //
418 // The "obvious" converting constructor implementation:
419 //
420 // template<class Y>
421 // weak_ptr(weak_ptr<Y> const & r): px(r.px), pn(r.pn) // never throws
422 // {
423 // }
424 //
425 // has a serious problem.
426 //
427 // r.px may already have been invalidated. The px(r.px)
428 // conversion may require access to *r.px (virtual inheritance).
429 //
430 // It is not possible to avoid spurious access violations since
431 // in multithreaded programs r.px may be invalidated at any point.
432 //
433
434 template <typename Y> CComWeakPtr(CComWeakPtr<Y> const & r): pn(r.pn) // never throws
435 {
436 px = r.lock().get();
437 }
438
439 template <typename Y> CComWeakPtr(CComSharedPtr<Y> const & r): px(r.px), pn(r.pn) // never throws
440 {
441 }
442
443 template <typename Y> CComWeakPtr & operator=(CComWeakPtr<Y> const & r) // never throws
444 {
445 px = r.lock().get();
446 pn = r.pn;
447 return *this;
448 }
449 template <typename Y> CComWeakPtr & operator=(CComSharedPtr<Y> const & r) // never throws
450 {
451 px = r.px;
452 pn = r.pn;
453 return *this;
454 }
455
456 CComSharedPtr<T> lock() const // never throws
457 {
458 #if defined(BOOST_HAS_THREADS)
459
460 // optimization: avoid throw overhead
461 if(expired())
462 {
463 return CComSharedPtr<T>();
464 }
465
466 try
467 {
468 return CComSharedPtr<T>(*this);
469 }
470 catch(boost::bad_weak_ptr const &)
471 {
472 // Q: how can we get here?
473 // A: another thread may have invalidated r after the use_count test above.
474 return CComSharedPtr<T>();
475 }
476
477 #else
478
479 // optimization: avoid try/catch overhead when single threaded
480 return expired()? CComSharedPtr<T>(): CComSharedPtr<T>(*this);
481
482 #endif
483 }
484
485 long use_count() const // never throws
486 {
487 return pn.use_count();
488 }
489
490 bool expired() const // never throws
491 {
492 return pn.use_count() == 0;
493 }
494
495 void reset() // never throws in 1.30+
496 {
497 this_type().swap(*this);
498 }
499
500 void swap(this_type & other) // never throws
501 {
502 std::swap(px, other.px);
503 pn.swap(other.pn);
504 }
505
506 // Tasteless as this may seem, making all members public allows member templates
507 // to work in the absence of member template friends. (Matthew Langston)
508
509 #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
510
511 private:
512
513 template<class Y> friend class CComWeakPtr;
514 template<class Y> friend class CComSharedPtr;
515
516 #endif
517
518 T * px; // contained pointer
519 com_weak_count pn; // reference counter
520
521 };
522 HRESULT CreateComTestInstance(const IID& iid,void** ppv)
523 {
524 ComTest *t = new ComTest;
525 HRESULT hr = t->QueryInterface(iid,ppv);
526
527 t->Release();
528 return hr;
529 }
530 class SharedTest
531 {
532
533 public:
534 CComSharedPtr<IPrintTest> spCom;
535 CComWeakPtr<IUnknown> wkcom;
536 };
537 /*
538 *测试多线程的方法,线程安全测试用
539 * void* testpthread(void* voidCom)
540 {
541 CComSharedPtr<IPrintTest> spCom = *((CComSharedPtr<IPrintTest>*)voidCom);
542 int i;
543
544 usleep(1);
545 for(i=0;i<5;i++)
546 {
547 CComSharedPtr<IPrintTest> spComtest;
548 spComtest = spCom;
549 spComtest->printref();
550 }
551
552 CComSharedPtr<IPrintTest> spComtest2[5];
553 for(i=0;i<5;i++)
554 {
555 spComtest2[i] = spCom;
556 spComtest2[i]->printref();
557 }
558 return 0;
559 }
560 */
561 int main()
562 {
563 SharedTest mysharedtest;
564 {
565
566 CComSharedPtr<IPrintTest> spIPT;
567 HRESULT hr = CreateComTestInstance(IID_IPrintTest,(void**)&spIPT);
568 spIPT.init_com_count();
569 if (SUCCEEDED(hr))
570 {
571 }
572 mysharedtest.spCom = spIPT;
573 mysharedtest.wkcom = spIPT;
574 {
575 CComSharedPtr<IPrintTest> spIPTfrom_wk(mysharedtest.wkcom.lock(),boost::detail::dynamic_cast_tag());
576 }
577 }
578 /* 测试多线程的线程安全
579 * pthread_t pid[10];
580
581 for(int i=0;i<10;i++)
582 {
583 pthread_create(&pid[i],NULL,testpthread,(void*)&mysharedtest.spCom);
584 }
585 usleep(1000);
586 for(int i=0;i<10;i++)
587 {
588 pthread_join(pid[i],NULL);
589 }
590 */
591 mysharedtest.spCom.reset();
592 CComSharedPtr<IPrintTest> spIPTfrom_wk2(mysharedtest.wkcom.lock(),boost::detail::dynamic_cast_tag());
593 if (spIPTfrom_wk2)
594 {
595 }
596 else
597 {
598
599 printf("get strong ref failed. The ref has been deleted.\n");
600 mysharedtest.wkcom.reset();
601 }
602 return 0;
603 }
604