<T>unique_ptr.get() 方法在使用原始指针赋值时调用析构函数?
unique_ptr<T>.get() method call destructor while assigning with raw pointer?
下面的程序使用std::unique_ptr<T>
来避免手动内存管理。我尝试了两种方法来实现它。问题是在第二种方法中,在分配给原始指针之前,要调用析构函数。这会导致程序崩溃,因为后面的代码试图访问无效内存。
我在第二种方法中的意图是,如何在现有的代码库中使用智能指针,以便我可以利用智能指针提供的自动内存管理。因此,我没有更改声明中指针的类型(即从Widget* w
更改为std::unique_ptr<Widget> w
)。
有人能详细解释一下吗?最好的做法是什么?。或者我错过了什么?。
#include<iostream>
#include<memory>
class Widget {
public:
Widget() { std::cout << "Widget::Widget()" << std::endl; }
virtual ~Widget() { std::cout << "Widget::~Widget()" << std::endl; }
virtual void draw() = 0;
};
class WindowsButton : public Widget {
public:
WindowsButton() = default;
~WindowsButton() = default;
void draw() { std::cout << "WindowsButton"<<std::endl; }
};
int main() {
// Working Code
// std::unique_ptr<Widget> w = std::unique_ptr<Widget>(new WindowsButton());
// w.get()->draw();
//In this way program is crashing while calling the w->draw()
Widget* w = std::unique_ptr<Widget>(new WindowsButton()).get();
w->draw();
}
在第二种情况下,您将创建一个unique_ptr
的临时实例,并在其上调用get()
成员函数。unique_ptr
对象将在完整表达式的末尾(分号处)销毁。
Widget* w = std::unique_ptr<Widget>(new WindowsButton()).get();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
// unnamed temporary instance destroyed here
当然,当实例被销毁时,unique_ptr
将delete
托管对象,并调用~Widget()
。
w->draw();
将取消引用指向无效内存位置的指针,从而导致未定义的行为。
也许您误解了unique_ptr
。这不是帮助管理原始指针的内存,而是替换原始指针。在这两个例子中,您都可以在unique_ptr
上调用get()
来获得原始poitner并使用它。这是不需要的。除了复制和调用delete
之外,您可以使用unique_ptr
执行与原始指针相同的操作。
你在第二个例子中遇到的问题是一个寿命问题。由于unique_ptr
拥有它所指向的对象,它控制着它的生存期,这意味着当unique_ptr
被销毁时,对象必须被销毁。由于unique_ptr
是临时的,它会破坏同一行中的对象。
因此,临时unique_ptr
没有什么用处,除非您使用它们初始化另一个unique_ptr
、shared_ptr
或其他接管所有权的对象,从而使unique_ptr
为空。
在以下几行中,我将稍微完善一下您的"工作代码":
std::unique_ptr<Widget> w = std::unique_ptr<Widget>(new WindowsButton());
w.get()->draw();
从安全性和语义的角度来说,这是可以的,你在这里没有做任何坏事。但正如我所说,对get()
的调用是不必要的,显式复制初始化也是如此:
std::unique_ptr<Widget> w(new WindowsButton());
w->draw();
这是C++03风格的使用方式,对C++11来说也可以。对于如何以及何时使用auto
和统一初始化等C++11功能,人们给出了不同的建议,因此这里有几个在C++11中更好的例子:
std::unique_ptr<Widget> w{new WindowsButton{}};
甚至
auto w{std::unique_ptr<Widget>{new WindowsButton{}}};
(我认为这相当丑陋)。
在C++14中有make_unique
,建议几乎永远不要使用new
,所以您的初始化看起来像这样:
auto w{std::make_unique<WindowsButton>()};
这里,w
具有类型std::unique_ptr<WindowsButton>
,但这通常不会有什么影响,因为如果需要,它可以转换为std::unique_ptr<Widget>
。
在这一行中,
Widget* w = std::unique_ptr<Widget>(new WindowsButton()).get();
CCD_ 29是临时对象并且在该行执行完成时被破坏。因此,Widget
也被破坏。这解释了当你调用时的分段违规
w->draw();
此时,w
是一个悬空指针。
- 从堆栈分配的原始指针构造智能指针
- 将unique_ptr分配给原始指针
- 如何将唯一指针的 std::vector 转换为原始指针的 std::span?
- <Base> <Derived> 具有相同原始指针共享引用的 shared_ptr 和 shared_ptr 实例是否计数?
- C++原始指针和"delete"
- 为什么 C++ 地址中的矢量无法通过原始指针访问
- 如何正确实现具有原始指针的类的复制构造函数?
- 如何在将原始指针移动到基类构造函数之前从unique_ptr中提取原始指针
- 为包含原始指针的对象C++智能指针
- c++:复制、删除和运算符=在原始指针映射中
- C++模板,用于通过常量引用和原始指针传递向量
- C++为什么原始指针不会增加shared_ptr的引用计数?
- 从shared_ptr获取原始指针以将其传递给需要 raw 的函数
- 如何包装多级原始指针以赋予其容器语义
- 为什么我可以通过原始指针而不是shared_ptr来修改对象
- 将原始指针传递给接受unique_ptr作为参数的函数
- 为什么我们不允许将纯引用参数传递给 std::thread,但允许传递原始指针?
- 更改保留指向其字段的原始指针的对象地址
- 从原始指针(衰减的 C 样式数组)和大小生成范围::视图
- 如何在C++代码中灵活使用和替换标准::shared_ptr或标准::unique_ptr或原始指针?