【问题标题】:Adding functionality to a handle wrapper向句柄包装器添加功能
【发布时间】:2009-11-09 20:41:41
【问题描述】:

我有一个 C++ RAII 类,用于使用 boost::shared_ptr 管理 Win32 HANDLE,看起来有点像这样:

namespace detail {
struct NoDelete { void operator()( void* ) {}; };
}; // namespace detail

template< typename HANDLE_TYPE, typename HANDLE_DELETER >
class CHandleT
{
public :
    explicit CHandleT( HANDLE_TYPE handle, bool delete_on_release = true )
    {
        if( delete_on_release )
            handle_ = Handle( handle, HANDLE_DELETER() );
        else
            handle_ = Handle( handle, detail::NoDelete() );

    };

    operator HANDLE_TYPE() const { return static_cast< HANDLE_TYPE >( handle_.get() ); };

protected:
    typedef boost::shared_ptr< void > Handle;
    Handle handle_;

}; // class CHandleT

struct DeallocateHandle
{
    void operator()( void* handle ) { ::CloseHandle( handle ); };
};

typedef CHandleT< HANDLE, DeallocateHandle > CHandle;

我想扩展它,而不是写:

CHandle my_handle( ::CreateEvent( NULL, FALSE, FALSE, NULL ) );
::SetEvent( my_handle.get() );

我会写:

CEvent my_event( NULL, FALSE, FALSE, NULL );
my_event.SetEvent();

最好的方法是使用 CHandle 类作为 CEvent 类的成员吗?

class CEvent
{
public:
    explicit CEvent( LPSECURITY_ATTRIBUTES lpEventAttributes = NULL,
                     BOOL bManualReset = TRUE,
                     BOOL bInitialState = FALSE,
                     LPCTSTR lpName = NULL, 
                     bool delete_on_release = true ) :
        handle_( new CHandle( ::CreateEvent( lpEventAttributes, 
                                              bManualReset, 
                                              bInitialState, 
                                              lpName ),
                               delete_on_release ) )
    {
    };

    BOOL SetEvent()
    {
        _ASSERT( NULL != handle_ && NULL != handle_.get() );
        return ::SetEvent( handle_.get() );
    };

private:
    boost::shared_ptr< CHandle > handle_;
}; // class CEvent

或者,有没有更好的方法? (请注意,我仍然想维护 boost::shared_ptr 给出的 CHandle 的复制语义。

谢谢, 保罗H

【问题讨论】:

    标签: c++ winapi raii


    【解决方案1】:

    我不会讨论 boost::shared_ptr 或任何智能 ptr。这里有几个原因,从不同的角度来看,为什么智能指针总是可以并且总是被冗余或被淘汰。

    代码似乎确实模拟了 CLR 和 NT 模型,在这种情况下,操作系统为您正在执行的操作提供了预定义的语义。它被称为 ::DuplicateHandle 并且它运行良好并且更适合跨进程场景(并且比 boost::interprocess 或类似的破解更少)。它适用于少数其他情况。

    现在是第二个,希望不是一个相反的位,其中可怜的旧 OO 继承被忽略,因为包含焦点经常胜出(然而,当你为那些不断尖叫的人玩混音时,它实际上与 OO 无关:包含我)。因此,无论它多么罕见,或者它是 OO、非 OO 还是 O(o) 参数:“继承”在这里获胜。

    为什么?因为它是一个可等待句柄的概念,包括 Win32 事件、互斥体、自动重置类型、线程,所有这些除了critical_section(它也有一个深层的支持句柄 - 但在 CLR 和 NT 中都经过特殊处理,加上它有双重性)。因此,它绝对有意义:

    typedef CHandleT WaitHandle;

    成为“层次结构”的根,以及底层实现的复制语义。

    最后,它是句柄数据类型的最有效表示,因为它们将模仿您的目标操作系统,并且不需要引用计数或雪崩/涟漪引用计数。

    然后是跨平台开发和 boost::thread-ing 并毁掉了睡前故事:-)

    【讨论】:

    • @Majk-a-Ra,为什么继承会获胜?如果他将 CHandleT 用作“scoped_handle”并且不需要多态性。更少的耦合选项应该是赢家。
    • 它被引用了,你确实提供了正确的关键字:组合。相反,混入不是耦合。您可以集中更换手柄类型。并且做更多的事情。就像标题和模板不耦合一样。那些喜欢他们的 .cpp 的人通常也喜欢包含 :) 基于组合的技术是最被低估的技术之一,但是:它在功能空间中不是问题。 OO 家伙需要更多地质疑这一点,为什么不呢。即使在算法中,当然,两者都没有用,但组合以函数形式突出,不继承或包含,不处理多态性..
    • @Majk-a-Ra 所以,如果我理解正确,你是在建议我应该让 CEvent 从 CHandle 派生?在这种情况下,CHandleT 需要一个虚拟析构函数吗? (为什么?/为什么不呢?)而且,我很想知道您对上述 fnieto 问题的看法。谢谢
    • 关于多态性,它有它的用途和很多场景,但是当它没有的时候,它真的没有 any :) 例如,我喜欢包含 typedef,那就是根本不包含:) 但如果需要多态性,你的权利。在这个例子中,我们正在处理一个操作系统,它当然完全没有 OO。它使用了 Stepanov 所说的非凡的机器抽象成就:C。虽然您可以用它来模拟多态性,而且它们通常在低级代码中进行,但 C++ 在使用类型和非常强大或特定的约束方面提供了巨大的好处。
    • 我会使用 boost,说真的,一切都完成了。但是如果我出于某种原因这样做,是的,我会避免使用 fnieto 提及,并为此禁止多态性(这很有意义,你总是可以将 ptrs 存储在容器中)。那么你能保证下面指定的 fnieto 是这种情况吗?这取决于您的选择,但如果经验较少的开发人员会使用相同的代码,请倾向于包含。
    【解决方案2】:

    您不需要将句柄作为 shared_ptr 存储在 CEvent 中。句柄已通过 CHandleT 属性共享。

    只要您不想将 CEvent 元素用作 CHandleT 元素(使用多态性),组合就可以了。

    【讨论】:

      猜你喜欢
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-15
      • 2010-11-22
      • 2010-09-07
      • 2017-12-06
      相关资源
      最近更新 更多