C ++来自同一原始指针的多个唯一指针

时间:2018-03-17 10:33:47

标签: c++ c++11 pointers unique-ptr

请考虑下面的代码。我对唯一指针的理解是只能使用一个唯一指针来引用一个变量或对象。在我的代码中,我有多个unique_ptr访问同一个变量。

显然,这不是使用智能指针的正确方法,因为指针应该具有完整的创建所有权。但是,为什么这有效并且没有编译错误?感谢。

#include <iostream>
#include <memory>

using namespace std;

int main()
{
    int val = 0;
    int* valPtr = &val;

    unique_ptr <int> uniquePtr1(valPtr);
    unique_ptr <int> uniquePtr2(valPtr);

    *uniquePtr1 = 10;
    *uniquePtr2 = 20;

    return 0;
}

3 个答案:

答案 0 :(得分:5)

  

但是,为什么这是有效的

有效!它是未定义的行为,因为std::unique_ptr的析构函数将释放具有自动存储持续时间的对象。

实际上,您的程序会尝试三次销毁int对象。首先是uniquePtr2,然后是uniquePtr1,然后是val本身。

  

没有编译错误?

因为在编译时通常无法检测到这些错误:

unique_ptr <int> uniquePtr1(valPtr);
unique_ptr <int> uniquePtr2(function_with_runtime_input());

在此示例中,function_with_runtime_input()可能会执行许多复杂的运行时操作,最终会返回指向同一对象valPtr的指针。

如果您正确使用std::unique_ptr,那么您几乎总是会使用std::make_unique来防止此类错误。

答案 1 :(得分:1)

只是Christian Hackl出色答案的补充:

引入了

std::unique_ptr以确保RAII指针;这意味着,与原始指针相反,您不必再自己关注破坏了。原始指针的整个管理由智能指针完成。被遗忘的delete引起的泄漏不再发生。

如果std::unique_ptr只允许由std::make_unique创建,那么在分配和释放时绝对安全,当然在编译期间也可以检测到。

但事实并非如此:std::unique_ptr也可以使用原始指针构建。原因是,能够使用硬指针构造使std::unique_ptr更有用。如果这是不可能的,例如Christian Hackl function_with_runtime_input()返回的指针不可能融入现代RAII环境,你必须自己处理破坏。

当然,这样做的缺点是像你这样的错误可能会发生:std::unique_ptr无法忘记破坏,但总是有可能出现错误的多次破坏(编译器无法跟踪,因为CH已经说过了),如果您使用原始指针构造函数参数创建它。始终要注意std::unique_ptr逻辑上取得了原始指针的“所有权” - 这意味着除了std::unique_ptr本身之外没有其他人可以删除指针。

根据经验,可以说:

  • 如果可能,请始终使用std::unique_ptr创建std::make_unique
  • 如果需要使用原始指针构建,请在创建std::unique_ptr之后再触摸原始指针。
  • 请务必注意,std::unique_ptr取得所提供的原始指针的所有权
  • 仅提供堆的原始指针。永远不要使用指向本地的原始指针 堆栈变量(因为它们将不可避免地自动销毁, 比如你的例子中的val
  • 仅使用由std::unique_ptr创建的原始指针创建new,如果可能的话。
  • 如果std::unique_ptr需要使用由new之外的其他内容创建的原始指针构建,请向std::unique_ptr添加自定义删除器,该删除器与硬指针创建者匹配。一个例子是(基于C的)FreeImage库中的图像指针,它总是必须被FreeImage_Unload()
  • 销毁。

这些规则的一些例子:

// Safe
std::unique_ptr<int> p = std::make_unique<int>();

// Safe, but not advisable. No accessible raw pointer exists, but should use make_unique. 
std::unique_ptr<int> p(new int());

// Handle with care. No accessible raw pointer exists, but it has to be sure
// that function_with_runtime_input() allocates the raw pointer with 'new'
std::unique_ptr<int> p( function_with_runtime_input() );

// Safe. No accessible raw pointer exists,
// the raw pointer is created by a library, and has a custom
// deleter to match the library's requirements
struct FreeImageDeleter {
    void operator() (FIBITMAP* _moribund) { FreeImage_Unload(_moribund); }
};
std::unique_ptr<FIBITMAP,FreeImageDeleter> p( FreeImage_Load(...) );

// Dangerous. Your class method gets a raw pointer
// as a parameter. It can not control what happens
// with this raw pointer after the call to MyClass::setMySomething()
// - if the caller deletes it, your'e lost.
void MyClass::setMySomething( MySomething* something ) {
   // m_mySomethingP is a member std::unique_ptr<Something>
   m_mySomethingP = std::move( std::unique_ptr<Something>( something ));
}

// Dangerous. A raw pointer variable exists, which might be erroneously
// deleted multiple times or assigned to a std::unique_ptr multiple times.
// Don't touch iPtr after these lines!
int* iPtr = new int();
std::unique_ptr<int> p(iPtr);

// Wrong (Undefined behaviour) and a direct consequence of the dangerous declaration above.
// A raw pointer is assigned to a std::unique_ptr<int> twice, which means
// that it will be attempted to delete it twice.
// This couldn't have happened if iPtr wouldn't have existed in the first
// place, like shown in the 'safe' examples.
int* iPtr = new int();
std::unique_ptr<int> p(iPtr);
std::unique_ptr<int> p2(iPtr);


// Wrong. (Undefined behaviour)
// An unique pointer gets assigned a raw pointer to a stack variable.
// Erroneous double destruction is the consequence
int val;
int* valPtr = &val;
std::unique_ptr<int> p(valPtr);

答案 2 :(得分:0)

这个代码示例有点人为。 unique_ptr通常不会在现实代码中以这种方式初始化。使用std::make_unique或初始化unique_ptr而不将原始指针存储在变量中:

unique_ptr <int> uniquePtr2(new int);