【问题标题】:Multiple instances of a unique_ptrunique_ptr 的多个实例
【发布时间】:2016-02-25 08:34:05
【问题描述】:

在发现我需要 delete 我创建的任何 new 指针后,我很快意识到我的项目充满了内存泄漏,我什至不知道。所以我被提示使用智能指针。但是,在尝试创建智能指针的多个实例时我遇到了问题。我创建了一个 SSCE 来更好地解释这一点。

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <memory>
#include "classa.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    std::unique_ptr<ClassA> classa; //<----- a smart pointer of a Class type
};

#endif // MAINWINDOW_H

主窗口.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "classa.h"
#include <QDebug>

QVector<ClassA*> classes;//<------ QVector that contains instances of ClassA
                                    //So I can retrieve them later based on an index.

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    classa = std::unique_ptr<ClassA>(new ClassA()); //<---- Created here
    classes.push_back(classa.get()); //<---- appended to the QVector
}

MainWindow::~MainWindow()
{
    delete ui;
}

//When the button is clicked, the program crashes when trying to qDebug
//because the first instance that was loaded in the QVector is no longer
//"valid." It stops being "valid" when I create the second instance of ClassA
void MainWindow::on_pushButton_clicked()
{
    classa = std::unique_ptr<ClassA>(new ClassA());
    classes.push_back(classa.get());

    qDebug() << classes.at(0);
}

希望上面的 cmets 能解释清楚。想到的第一个解决方案是将我在 mainwindow.h 中的声明更改为 std::unique_ptr&lt;ClassA&gt; *classa;,但这不会破坏智能指针的目的吗?

真的很困惑。感谢您的宝贵时间。

【问题讨论】:

  • 不要将指针存储在像QVector 这样的容器中。如果您不使用任何多态性,那只会让一切变得困难。直接存储ClassA。如果确实需要存储基类指针,请创建QVector&lt;std::unique_ptr&lt;ClassA&gt;,或使用any of Qt's smart pointers
  • 你好。问题(我相信)不在于我如何选择存储它们。我已经尝试过你的建议。它仍然使第一个实例“无效”,并且只要我靠近它就会崩溃。我什至将第一个实例放在ClassA* variable 中,当我在创建第二个实例后调用它时,它崩溃了。
  • 感谢您编写一段代码。但是... A) 你的问题与 Qt 有什么关系?没有。您可以编写一个不涉及 Qt、类继承等的示例。B) 实际问题是什么? 是的,它就在代码中,但您不能描述 呢?我们真的必须从源头找出预期行为/观察到的行为吗?
  • 通过从智能指针中提取哑指针并将其存储起来供以后使用,您就破坏了智能指针的用途。
  • 有很多事情,只是不是你做了什么。 unique_ptr 的全部意义在于它是唯一的。我们真的不知道你真正的问题是什么。你为什么要使用指针?我们不知道。 classa 有什么用途?我们不知道。

标签: c++ qt class smart-pointers


【解决方案1】:

我认为你的困惑的核心是 std::unique_ptr&lt;&gt; 实际上做了什么

来自您的 MainWindow 构造函数:

classa = std::unique_ptr<ClassA>(new ClassA());

classa 现在(巧妙地)指向ClassA 实例#1。 如果内存引用超出范围,内存将被删除,避免内存泄漏。

classes.push_back(classa.get());

classes[0] 现在拥有一个指向实例 #1 的(哑)指针。

从您的按钮单击事件处理程序:

classa = std::unique_ptr<ClassA>(new ClassA());

classa 现在(智能地)指向ClassA 实例#2。

通过为classa 分配新值,旧值超出范围。为实例 #1 分配的内存现在已释放。这就是智能指针的用途

classes[0] 仍然持有指向实例#0 持有的内存先前的(哑)指针。 (你知道这是怎么回事,不是吗?)

classes.push_back(classa.get());

classes[1] 现在拥有一个指向实例 #2 的(哑)指针。

qDebug() << classes.at(0);

未定义的行为,你死了。


如果您的内存由智能指针管理——正如你应该的那样——它由智能指针管理。不要 .get() 将这些愚蠢的指针从它们中取出并将它们存储在其他地方。


由于您的示例没有告诉我们classes 向量应该使用做什么,因此目前很难给出适当的建议。

  • 您可以使用vector&lt; ClassA &gt; 直接存储ClassA 的实例。
  • 您可以使用vector&lt; std::shared_ptr&lt; ClassA &gt; &gt; 来存储智能指针,也可以在其他地方使用它们。(*)

或者……

  • 您可以告诉我们您正在尝试解决的实际问题,我们可以告诉您最好的容器是什么。 ;-)

(*):

std::shared_ptr&lt;&gt;std::unique_ptr&lt;&gt; 更强大的兄弟。唯一指针是唯一的,它们不能被复制。从好的方面来说,它们与您手动 delete 的“普通”指针一样有效。

std::shared_ptr&lt;&gt; 添加一个(微小)位记账,即存在的副本数(因为它可以被复制),并且只有在 last 时才删除内存em> 智能指针的副本超出范围。

要么依赖存储(和使用)他们包含的指针。

【讨论】:

  • 感谢您的回答!我现在更好地理解了智能指针的作用。让我测试一些东西
  • @mc360pro:这就是我在评论您的代码示例时的意思:它包含相当多的与问题无关的代码,但并没有真正显示问题。您希望您的 MainWindow 保存一个 ClassA 对象,但 将该对象存储(引用)在一个(全局可见的)向量中。这破坏了数据封装,即糟糕的设计,我们甚至不知道你想用它实现什么。您的问题不仅与智能指针有关;您正在尝试解决一个不相关的问题,并且可能以次优的方式。 ;-)
  • 我现在明白了。谢谢你的建议。无论出于何种原因,我的印象是,为了在一个类中创建一个对象的多个实例,它必须被声明为一个指针。哇。这使一切变得容易得多。很抱歉给您带来麻烦和困惑,并感谢您(和其他所有人)的时间:)
  • @mc360pro:没问题,这就是我们来这里的目的。 ;-)
  • @mc360pro:相信一件事。做得对,C++ 简单。如果您觉得某事难​​以应付,那要么是因为您试图解决的问题过于复杂(即,不是语言的错),要么是您重新尝试以错误的方式解决它(即,您还没有学习该语言的特定部分,请问)。我会提出这种特殊要求的语言很少。 ;-)
【解决方案2】:
classa = std::unique_ptr<ClassA>(new ClassA()); //<---- Created here
classes.push_back(classa.get()); //<---- appended to the QVector

不要这样做。如果您打算使用unique_ptr 来管理对象的生命周期,请不要在其他地方存储指向该对象的其他指针。如果你这样做了,只要 unique_ptr 消失,其他指针就会悬空。

您并没有真正说明您的外部问题,但无论如何,拥有unique_ptr 并存储指向同一对象的哑指针的方法是自找麻烦。如果您没有切片问题,则不使用指针可能是更好的方法。如果你这样做了,要么只保留一个指针,要么使用shared_ptr 可能是解决方案。

为什么你需要指针?价值观有什么问题?

【讨论】:

  • 感谢您的回答。我使用指针是因为我需要 ClassA 对象的多个实例。通常,我会将ClassA *classa 放在我的头文件中,然后从中创建new 实例。
  • 为什么需要指针来拥有多个实例?为什么不能,例如:std::vector&lt;SomeClass&gt; j; j.push_back(1); j.push_back(2);?指针的用途是什么?
  • 我认为类的所有实例共享静态变量
  • 静态变量不与类的任何实例相关联。这和指针有什么关系?另外,什么是“问题”? classa 有什么用途?为什么它存在?
  • @mc360pro: 你的例子没有静态变量...?!?
【解决方案3】:

您应该阅读更多关于智能指针的一般知识、它们是什么以及它们是如何工作的。 关于unique_ptr 的简短版本是这样的:当发生以下两种情况之一时,包装对象被销毁并释放内存:

  1. unique_ptr 本身已被销毁(例如,超出范围)
  2. unique_ptr 被分配了另一个对象(由operator=reset())。

在你的情况下,事情是这样发生的:

  • 您正在创建一个对象并将其分配给构造函数中的unique_ptr
  • 您还在向量中存储了指向该对象的原始指针
  • 稍后,在您的按钮单击处理程序中,您创建一个新对象并将其分配给unique_ptr
  • 这会导致之前的对象被销毁
  • 此时,您存储在向量中的指针悬空,它指向一个已销毁的对象

每次调用单击处理程序时都会重复这一系列步骤:您在unique_tr 中创建并存储一个新对象,这会导致前一个对象被销毁,这意味着您的向量将只包含悬空指针(除了最后一个)。

这应该足以让您开始寻找解决方案。

【讨论】:

  • 感谢您的回答。是的,我想我了解导致问题的事件的行为,我正在寻找解决方案。我想要一种使用智能指针的内存优势创建多个 ClassA 实例的方法。这不可能吗?
  • 在不知道为什么需要指针的情况下,我认为没有人能够为您提供有用的答案。为什么不只使用值?指针的用途是什么?
  • @mc360pro:尝试将unique_ptrs 存储在向量中,而不是原始指针。为此,您必须了解 unique_ptr 实现的移动语义和单一所有权模型。或者从共享所有权智能指针(例如shared_ptr)开始,让您的代码使用它,然后尝试回到单一所有权模型并使其工作。并且始终小心将智能指针和原始指针混合到同一个对象。
猜你喜欢
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 2015-10-20
相关资源
最近更新 更多