std::shared_ptr 在 Qt 的 getter 函数中删除

std::shared_ptr deleted in a getter function with Qt

我正在为我的 Qt 项目使用共享指针,但是当我通过共享指针调用 setLayout() 时出现分段错误。

我是这样称呼它的:

 model->getBody()->setLayout(layoutTemplate.get());

函数getBody()returns一个std::shared_ptr:

std::shared_ptr<QWidget> MainTemplate::getBody()
{
   return std::shared_ptr<QWidget>(ui->body);
}

我尝试使用 QSharedPointer 而不是 std::shared_ptr,但结果相同。

为什么我的指针被删除了?

Since the shared_ptr only lives until the end of that statement1 the next time you try to use ui->body it will have been deleted.

您可能不想在 getter 中创建新的共享所有权。相反,得到的任何东西都应该作为 shared_ptr 存在,而 getter 可以 return 一个副本(shared_ptr)。

或者,在您不转移所有权的情况下,原始指针(其中 body 可以为 null)或引用(其中 body 不能为 null)更合适。 pointers/references 不会超过 pointed/referenced 对象的通常注意事项仍然适用。

  1. 并且没有复制它

智能指针表示对象的所有权shared_ptr 特别代表了一个由多个实体拥有的对象,并且必须一直存在到每个人都用完它为止。每当指向一个对象的最后一个 shared_ptr 被销毁时,它指向的对象也被销毁。 shared_ptr的正确用法是:

std::shared_ptr<T> ptr_a(new T); // Create a T and immediately pass it to shared_ptr
std::shared_ptr<T> ptr_b = std::make_shared<T>(); // Same effect but more efficient

ptr_a = ptr_b; // smart pointer to smart pointer assignment (never existing raw to smart)
ptr_a.reset(new T);            // This is fine.
ptr_a = std::make_shared<T>(); // Same as previous line. Again more efficient

//... eventually ptr_a and ptr_b go out of scope and handle deleting their objects

请注意,在所有重新分配的示例中,ptr_a 检查引用计数并删除持有的对象,如果它是最后一个 shared_ptr 指向它。

通常,您只想在创建智能指针的确切时刻将原始指针传递给智能指针。您所做的是将对象的 所有权 传递给临时智能指针。在语句之后,临时 shared_ptr 被销毁,并且由于从未制作过它的副本,它假定它应该销毁 ui->body。一般来说,你想要做的只是通过非常量引用 return body,或者只是不 return 它并为其参数提供 setter 函数。

QWidget& MainTemplate::getBody()
{
   return ui->body;
}

然而,如果某些原因决定 body 需要能够比它的 class 更长寿(值得怀疑,除非做出有问题的设计决定),您需要使 body 本身成为 shared_ptr 并且return 按值计算。这将创建 shared_ptr 的副本,并且可以保证它们中的最后一个将销毁该对象。但是你绝不能在主体构造后使用原始指针,也不能手动尝试删除它。

UI {    // whatever class MainTemplate::ui points to
private:
    std::shared_ptr<<QWidget> body;
...
}

MainTemplate {
public:
    std::shared_ptr<QWidget> MainTemplate::getBody()
    {
        return ui->body;
    }
}

同样,如果您的 classes 已经以实用的方式定义,我认为不需要这样做。此外,如果 ui 不属于 class,则可以将 ui 设为 shared_ptr,而将 return 设为 return 而不是其主体。然后,让调用者通过 returned shared_ptr<UI>.

访问正文