为什么智能指针不能用通常的指针方式声明



我已经阅读了有关初始化唯一指针的文档 这里.我尝试以相同的方式声明唯一指针(see unique_ptr<int> temp1 {&h},但我在文档中没有看到这种类型的声明,只是在实验(我声明了一个非智能指针。进行这个实验背后的想法是看看std::unique_ptr::get()方法是如何工作的。这是代码:

#include<iostream>
#include<stdio.h>
#include<memory>
using namespace std  ;
int main(){
int h {100};
unique_ptr<int> temp1 {&h};
cout<<"temp1.get() :"<<temp1.get()<<endl;
cout<< "&h : "<<&h<<endl;
cout<<"*temp : "<<*temp1<<endl;
return 0 ; 
}

代码编译,我得到以下输出:

temp1.get() :0x7ffd4322c5cc
&h : 0x7ffd4322c5cc
*temp : 100
/home/abhishek/.codelite/tmp/abhishek/codelite-exec.sh: line 3:  7889 Segmentation fault      (core dumped) ${command}
Hit any key to continue...

我可以看到std::unique_ptr::get()返回托管对象的地址,与&h相同。 这里的错误说什么? 尽管此处已讨论过为智能指针分配地址。它没有回答我的问题。

如下所述:

通过调用 get_deleter(((ptr( 使用可能由用户提供的删除程序来释放对象。默认删除程序使用 delete 运算符,该运算符销毁对象并解除分配内存。

unique_ptr保存指向存储在堆上的动态分配变量的指针。初始化int h时,将此变量存储在堆栈上。很明显,您不应该对未使用 new 动态分配的任何内容使用 delete,因此您必须这样做:

int* h_ptr = new int (100);
unique_ptr<int> temp1 {h_ptr};

如您链接的问题的答案之一中所述:

unique_ptr是指向对象的独占所有者。当它超出范围时,它将删除该对象。

也就是说,您传递给std::unique_ptr的指针需要完全由生成的std::unique_ptr拥有。这是因为一旦std::unique_ptr有了指针,它就会管理指针,并在std::unique_ptr超出范围时尝试delete指针。

但是,这里的情况并非如此。h是一个局部变量。一旦h超出范围,它就会被摧毁。但一旦temp1超出范围,它也会被摧毁。所以有两件事会试图摧毁它。这几乎肯定会导致未定义的行为,这可能是您在这里看到的。

这就是为什么您不应该将局部变量的地址传递给std::unique_ptr,而是传递动态分配的地址,例如通过newstd::make_unique()

为什么智能指针不能用通常的指针方式声明

std::shared_ptr/std::uniqure_ptr隐式持有对象的所有权,因此他们负责删除该对象,并且不允许您使用智能指针具有所有权delete删除对象。std::experimental::observer_ptr(TS v2( 和std::weak_ptr不拥有所有权。

另一端的原始指针既可以拥有,也可以不拥有,因此您需要记录该指针是否拥有所有权,以及具有该指针的指针是否负责在其上调用delete

因此,您对unique_ptr<int> temp1 {&h};执行的操作与您记录的原始指针相同,该原始指针是拥有原始指针,并且您在其上调用delete

int h {100};
int * temp1 = &h; // none owning pointer
// … some code here …
delete temp1; // calling delete on temp1 is not valid and would result in the same problem as with your unique_ptr example.

是否可以转让对象的所有权取决于存储持续时间

h自动落入组中:

自动存储持续时间。对象的存储在封闭代码块的开头分配,并在末尾解除分配。所有本地对象都有此存储持续时间,但声明为静态、外部或thread_local的对象除外。

截至此,所有权无法转让,因为它完全由封闭代码块拥有。

只能转让具有动态存储持续时间的对象:

动态存储持续时间。通过使用动态内存分配函数为每个请求分配和取消分配对象的存储。有关使用此存储持续时间初始化对象的详细信息,请参阅 new-expression。

(自动通常称为在堆栈上分配,动态通常称为在堆上分配。

最新更新