std::unique_ptr and pointer-to-pointer

9

我一直在使用std::unique_ptr存储一些COM资源,并提供了自定义的删除函数。然而,许多COM函数需要指向指针的指针。目前,我正在使用编译器中的_Myptr实现细节。如果直接访问这个数据成员,会破坏unique_ptr吗?或者我应该存储大量临时指针来构造unique_ptr rvalues?


1
为什么不使用 ATL::CComPtr_com_ptr_t - sharptooth
1
@sharptooth:我的应用程序只持有一个引用。没有必要为我的一个引用进行引用计数。 - Puppy
1
@DeadMG:没问题,但是你可能不知道其他COM对象已经获取了该对象并希望共享所有权。由于它们都设计支持引用计数,所以你不能将“我认为我是唯一拥有指针的人”与COM对象结合使用。 - sharptooth
2
@sharptooth:你误解了我的意思。只持有一个引用。其他引用仍然都被计算在内。而且这只是COM对象——与DirectX一起工作。 - Puppy
1
@sharptooth:我不需要那些库或功能,所以我为什么要用它们呢? - Puppy
显示剩余10条评论
4个回答

6

COM对象天生就可以通过引用计数来管理,因此在任何情况下都不应该使用除了引用计数智能指针之外的任何东西,如ATL::CComPtr_com_ptr_t,即使对于您的用例似乎不合适(我完全理解您的担忧,但我认为您赋予它们过多的重量)。这两个类旨在在使用COM对象时涉及所有有效场景,包括获取指向指针的指针。是的,这有点太多功能了,但如果您不期望任何特定的负面后果,您应该只使用这些类 - 它们恰好为此目的而设计。


这个。COM对象已经有引用计数了,所以无论你喜不喜欢,你所做的任何事情都将是在其之上多余的包装器。boost::unique_ptr并不是为此而设计的,因此任何试图将其塞入其中的尝试都会增加不必要的复杂性和错误的机会。CComPtr没有开销。只需使用它即可。 - Alan

4
我不久前也曾遇到同样的问题,并想出了两种不同的解决方案:
第一种是一个简单的包装器,封装了一个“可写”指针,并且可以通过std::move移动到我的智能指针中。这比使用你提到的临时指针略为方便,因为你不能直接在调用站点定义类型。
因此,我没有坚持使用它。所以我做了一个Retrieve帮助函数,会获取COM函数并返回我的智能指针(并在内部处理所有临时指针的事情)。如果您想在更复杂的东西上使用它,只需通过std::bind传递调用,并仅使要返回的指针自由即可。
我知道这不是您直接询问的内容,但我认为这是解决您遇到的问题的一个简洁解决方案。
顺便说一句,我更喜欢boost的intrusive_ptr而不是std::unique_ptr,但这总归是口味问题。
编辑:这里有一些示例代码,已从我的版本转换为使用boost::intrusive_ptr(因此可能无法直接与unique_ptr一起使用)。
template <class T, class PtrType, class PtrDel>
HRESULT retrieve(T func, std::unique_ptr<PtrType, PtrDel>& ptr)
{
  ElementType* raw_ptr=nullptr;

  HRESULT result = func(&raw_ptr);
  ptr.reset(raw_ptr);

  return result;
}

例如,可以像这样使用它:
std::unique_ptr<IFileDialog, ComDeleter> FileDialog;
/*...*/
using std::bind;
using namespace std::placeholders;
std::unique_ptr<IShellItem, ComDeleter> ShellItem;
HRESULT status = retrieve(bind(&IFileDialog::GetResult, FileDialog, _1), ShellItem);

为了加分,您甚至可以让retrieve返回unique_ptr而不是通过引用获取它。 bind生成的函数对象应该具有typedefs签名以派生指针类型。如果出现错误的HRESULT,则可以抛出异常。


1

C++0x智能指针有一种可移植的方法来获取原始指针容器.get()或完全释放它.release()。您也可以始终使用&(*ptr),但这不太惯用。

如果您想使用智能指针来管理对象的生命周期,但仍需要原始指针来使用不支持智能指针的库(包括标准c库),则可以使用这些函数最方便地获取原始指针。

请记住,您仍然需要在您希望对象存在的时间内保留智能指针(因此要注意其生命周期)。

类似于:

call_com_function( &my_uniq_ptr.get() ); // will work fine
return &my_localscope_uniq_ptr.get();    // will not
return &my_member_uniq_ptr.get();        // might, if *this will be around for the duration, etc..

注意:这只是对你问题的一般回答。如何最好地使用COM是一个单独的问题,Sharptooth可能是正确的。


你完全误读了问题。我需要一个指向unique_ptr内部指针的指针,而不仅仅是值。 - Puppy
unique_ptr 不提供直接访问内部指针的方式,因为这样做会极其不安全。智能指针的目的是为了保证安全。 - Alan

-1
使用类似这样的辅助函数。
template< class T >
T*& getPointerRef ( std::unique_ptr<T> & ptr )
{
    struct Twin : public std::unique_ptr<T>::_Mybase {};
    Twin * twin = (Twin*)( &ptr );
    return twin->_Myptr;
}

检查实现

int wmain ( int argc, wchar_t argv[] )
{   
    std::unique_ptr<char> charPtr ( new char[25] );
    delete getPointerRef(charPtr);
    getPointerRef(charPtr) = 0;

    return charPtr.get() != 0;
}

我刚刚注意到你在删除你所创建的 new[] - Puppy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接