unique_ptr

时间:2016-02-25 08:34:06

标签: c++ qt class smart-pointers

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

mainwindow.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

mainwindow.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);
}

希望以上评论足够解释。我想到的第一个解决方案是将mainwindow.h中的声明更改为std::unique_ptr<ClassA> *classa;,但是这不会破坏智能指针的用途吗?

真的很困惑。谢谢你的时间。

3 个答案:

答案 0 :(得分: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可能是解决方案。

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

答案 1 :(得分:2)

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

  1. unique_ptr本身被销毁(例如,超出范围)
  2. unique_ptr分配了另一个对象(operator=reset())。
  3. 在你的情况下,事情就是这样:

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

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

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

答案 2 :(得分:2)

我认为您混淆的核心是std::unique_ptr<>实际做什么

从您的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< ClassA >直接存储ClassA的实例。
  • 您可以使用vector< std::shared_ptr< ClassA > >存储智能指针,在其他地方使用它们。(*)

或者...

  • 您可以告诉我们您要解决的实际问题,我们可以告诉您最佳容器是什么。 ; - )

(*):

std::shared_ptr<>std::unique_ptr<>更强大的兄弟姐妹。唯一指针是唯一,它们无法复制。从好的方面来说,它们与您手动delete的“正常”指针一样高效。

std::shared_ptr<>添加了一小部分簿记,即存在的副本数量(因为可以被复制),并且只有最后一个才能删除内存智能指针的副本超出范围。

依赖

相关问题