一些std :: unique_ptr使用和“陷阱”

时间:2013-07-30 10:52:45

标签: c++ c++11 smart-pointers unique-ptr resource-management

有哪些C ++ 11 std::unique_ptr 使用和陷阱

我是否也可以使用std::unique_ptr存储动态分配的数组?

我是否可以使用std::unique_ptr使用自定义删除机制的资源?

1 个答案:

答案 0 :(得分:18)

让我们使用Q& A格式组织一些用途和陷阱。


Q1:我想将指针存储到班级Component内的班级X
我不希望“容器”类X可复制; XComponent个实例的唯一所有者 我知道拥有 原始指针是一件坏事,潜在的“漏洞”来源(而不是观察原始指针)。 我可以将智能指针用于此目的吗?

A1: C ++ 11的 std::unique_ptr 当然是个不错的选择。

唯一(非共享)所有权的情况下很好,并且没有std::shared_ptr的开销。
它是以前的C ++ 98/03 boost::scoped_ptr的绝佳替代品 事实上,此外,std::unique_ptr提供了移动语义 因此,如果类X包含unique_ptr<Component>个数据成员(以及其他可移动数据成员),则整个类X自动移动。

示例用法如下所示:

#include <memory> // for std::unique_ptr

class X
{
    std::unique_ptr<Component> m_pComponent;
    ....

public:
    X()
        : m_pComponent( new Component() )
    {
        ....
    }
}

(当然,作为智能指针,不需要在包含类的析构函数中显式删除它。)


Q2:太棒了!不需要显式的析构函数,没有std::shared_ptr开销(典型的C ++哲学:“我们不支付我们不使用的东西”),移动已经实现的语义机制!
但是,我遇到了一个问题:我的类Component有一个构造函数重载,它在创建Component实例之前需要在构造函数代码中计算一些参数。我尝试在构造函数中使用普通的operator=分配来将新创建的Component分配给unique_ptr,但是我收到了一条错误消息:

X::X()
{
    ....

    const int param = CalculateCoolParameter();

    // This assignment fails:
    m_pComponent = new Component(param); // <---- Error pointing to '=' here
                 ^--- error
}

A2:好的,可能你原本期望operator=超载释放以前拥有的 指针(如果有)并分配给新创建的指针 不幸的是,没有这样的过载 但是, std::unique_ptr::reset() 方法会执行!

m_pComponent.reset( new Component(param) );

Q3:嘿!这个unique_ptr非常酷! 我喜欢这样一个事实:它很聪明,它可以自动移动,并且不会带来开销 所以,我想用它来存储一些常量大小的动态分配的数组(在运行时计算),而不是使用std::vector(在这部分代码我是高度约束,我不想支付std:vector开销,因为我不希望所有std::vector动态调整大小功能,深层复制等。)

我试过这样的事情:

const size_t count = GetComponentsCount();
unique_ptr<Component> components( new Component[count] );

它编译得很好,但我注意到~Component析构函数只被调用一次,而不是我期望count析构函数调用!这里出了什么问题?

A3:问题在于,使用上述语法,std::unique_ptr使用delete来释放已分配的对象。但由于这些是使用new[]分配的,因此正确的清理调用是 delete[] (不是简单的delete没有括号)。

要解决此问题并指示unique_ptr正确使用delete[]释放资源,必须使用以下语法:

unique_ptr<Component[]> components( new Components[count] ); 
//                  ^^
//
// Note brackets "[]" after the first occurrence of "Component" 
// in unique_ptr template argument.
//

问题4:这太好了!但是,如果不使用普通C ++ unique_ptr(或delete)执行资源发布代码,而是使用某些自定义清理功能delete[] strong>,例如fclose()用于C <stdio.h>文件(使用fopen()打开),或CloseHandle()用于Win32文件HANDLE(使用CreateFile()创建)?

A4:这绝对可以:您可以为std::unique_ptr指定自定义删除

e.g:

// 
// Custom deleter function for FILE*: fclose().
//
std::unique_ptr<FILE,          // <-- the wrapped raw pointer type: FILE*
                int(*)(FILE*)> // <-- the custom deleter type: fclose() prototype
myFile( fopen("myfile", "rb"), // <-- resource (FILE*) is returned by fopen()
        fclose );              // <-- the deleter function: fclose()



//
// Custom deleter functor for Win32 HANDLE: calls CloseHandle().
//
struct CloseHandleDeleter
{
    // The following pointer typedef is required, since
    // the raw resource is HANDLE (not HANDLE*).
    typedef HANDLE pointer;

    // Custom deleter: calls CloseHandle().
    void operator()(HANDLE handle) const
    {
        CloseHandle(handle);
    }
};

std::unique_ptr<HANDLE, CloseHandleDeleter> myFile( CreateFile(....) );