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

 

你可能感兴趣的:(C++)