简体   繁体   English

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

[英]C++ multiple unique pointers from same raw pointer

Consider my code below. 考虑下面的代码。 My understanding of unique pointers was that only one unique pointer can be used to reference one variable or object. 我对唯一指针的理解是只能使用一个唯一指针来引用一个变量或对象。 In my code I have more than one unique_ptr accessing the same variable. 在我的代码中,我有多个unique_ptr访问同一个变量。

It's obviously not the correct way to use smart pointers i know, in that the pointer should have complete ownership from creation. 显然,这不是使用智能指针的正确方法,因为指针应该具有完全的创建所有权。 But still, why is this valid and not having a compilation error? 但是,为什么这有效并且没有编译错误? Thanks. 谢谢。

#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;
}

But still, why is this valid 但是,为什么这是有效的

It is not valid! 它是无效的! It's undefined behaviour, because the destructor of std::unique_ptr will free an object with automatic storage duration. 这是未定义的行为,因为std::unique_ptr的析构函数将释放具有自动存储持续时间的对象。

Practically, your program tries to destroy the int object three times. 实际上,您的程序试图破坏int对象三次。 First through uniquePtr2 , then through uniquePtr1 , and then through val itself. 首先通过uniquePtr2 ,然后通过uniquePtr1 ,然后通过val本身。

and not having a compilation error? 并没有编译错误?

Because such errors are not generally detectable at compile time: 因为在编译时通常无法检测到此类错误:

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

In this example, function_with_runtime_input() may perform a lot of complicated runtime operations which eventually return a pointer to the same object valPtr points to. 在此示例中, function_with_runtime_input()可以执行许多复杂的运行时操作,最终返回指向valPtr指向的相同对象的指针。


If you use std::unique_ptr correctly, then you will almost always use std::make_unique , which prevents such errors. 如果你正确使用std::unique_ptr ,那么你几乎总是会使用std::make_unique来防止这样的错误。

Just an addition to Christian Hackl's excellent answer: 只是Christian Hackl的优秀答案的补充:

std::unique_ptr was introduced to ensure RAII for pointers; 引入了std::unique_ptr以确保指针的RAII ; this means, in opposite to raw pointers you don't have to take care about destruction yourself anymore. 这意味着,与原始指针相反,您不必再自己关注破坏了。 The whole management of the raw pointer is done by the smart pointer. 原始指针的整个管理由智能指针完成。 Leaks caused by a forgotten delete can not happen anymore. 忘记delete造成的泄漏不再发生。

If a std::unique_ptr would only allow to be created by std::make_unique , it would be absolutely safe regarding allocation and deallocation, and of course that would be also detectable during compile time. 如果std::unique_ptr只允许由std::make_unique创建,那么在分配和释放时绝对安全,当然在编译期间也是可以检测到的。

But that's not the case: std::unique_ptr is also constructible with a raw pointer. 但事实并非如此: std::unique_ptr也可以用原始指针构造。 The reason is, that being able to be constructed with a hard pointer makes a std::unique_ptr much more useful. 原因是,能够使用硬指针构造使得std::unique_ptr更加有用。 If this would not be possible, eg the pointer returned by Christian Hackl's function_with_runtime_input() would not be possible to integrate into a modern RAII environment, you would have to take care of destruction yourself. 如果这是不可能的,例如Christian Hackl的function_with_runtime_input()返回的指针将无法集成到现代RAII环境中,您将不得不自己处理破坏。

Of course the downside with this is that errors like yours can happen: To forget destruction is not possible with std::unique_ptr , but erroneous multiple destructions are always possible (and impossible to track by the compiler, as CH already said), if you created it with a raw pointer constructor argument. 当然,这样做的缺点是像你这样的错误可能会发生:使用std::unique_ptr无法忘记破坏,但是错误的多次破坏总是可能的(并且编译器无法跟踪,正如CH已经说过的那样),如果你使用原始指针构造函数参数创建它。 Always be aware that std::unique_ptr logically takes "ownership" of the raw pointer - what means, that no one else may delete the pointer except the one std::unique_ptr itself. 始终要注意std::unique_ptr逻辑上取得了原始指针的“所有权” - 这意味着,除了一个std::unique_ptr本身之外,没有其他人可以删除指针。

As rules of thumb it can be said: 根据经验,可以说:

  • Always create a std::unique_ptr with std::make_unique if possible. 如果可能,始终使用std::make_unique创建std::unique_ptr
  • If it needs to be constructed with a raw pointer, never touch the raw pointer after creating the std::unique_ptr with it. 如果需要使用原始指针构造,则在创建std::unique_ptr之后永远不要触摸原始指针。
  • Always be aware, that the std::unique_ptr takes ownership of the supplied raw pointer 始终要注意, std::unique_ptr取得了所提供的原始指针的所有权
  • Only supply raw pointers to the heap. 仅提供堆的原始指针。 NEVER use raw pointers which point to local stack variables (because they will be unavoidably destroyed automatically, like val in your example). 永远不要使用指向本地堆栈变量的原始指针(因为它们将不可避免地自动销毁,例如示例中的val )。
  • Create a std::unique_ptr only with raw pointers, which were created by new , if possible. 如果可能的话,仅使用由new创建的原始指针创建std::unique_ptr
  • If the std::unique_ptr needs to be constructed with a raw pointer, which was created by something else than new , add a custom deleter to the std::unique_ptr , which matches the hard pointer creator. 如果std::unique_ptr需要使用由new其他东西创建的原始指针构造,则将自定义删除器添加到与硬指针创建者匹配的std::unique_ptr An example are image pointers in the (C based) FreeImage library, which always have to be destroyed by FreeImage_Unload() 一个例子是(基于C的) FreeImage库中的图像指针,它总是必须被FreeImage_Unload()销毁

Some examples to these rules: 这些规则的一些例子:

// 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);

This example of code is a bit artificial. 这个代码示例有点人为。 unique_ptr is not usually initialized this way in real world code. unique_ptr通常不会在现实代码中以这种方式初始化。 Use std::make_unique or initialize unique_ptr without storing raw pointer in a variable: 使用std::make_unique或初始化unique_ptr而不将原始指针存储在变量中:

unique_ptr <int> uniquePtr2(new int);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM