请考虑下面的代码。我对唯一指针的理解是只能使用一个唯一指针来引用一个变量或对象。在我的代码中,我有多个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;
}
答案 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);