• 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
  • 相关阅读:
    [最优化理论与技术]线性规划
    [吴恩达深度学习]神经网络和深度学习
    Linux系统级性能分析工具perf的介绍与使用
    Oracle表变化趋势追踪记录 & 表 历史统计信息查看
    delete noprompt archivelog
    DEPLOYING ORACLE RAC DATABASE 12C RELEASE 2 ON RED HAT ENTERPRISE LINUX 7
    How to Modify SCAN Setting or SCAN Listener Port after Installation (Doc ID 972500.1)
    How to create a RAC Database Service With Physical Standby Role Option? (Doc ID 1129143.1)
    Oracle级联备库0数据丢失--重建控制文件并应用主库online redo logfile的激活方法
    Handling ORL and SRL (Resize) on Primary and Physical Standby in Data Guard Environment (Doc ID 1532566.1)
  • 原文地址:https://www.cnblogs.com/absolute8511/p/1806734.html
Copyright © 2020-2023  润新知