unique_ptr 的多个实例
Multiple instances of a unique_ptr
在发现我需要 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;
但这不会破坏智能指针的目的吗?
真的很困惑。感谢您的宝贵时间。
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
可能是解决方案。
为什么你需要指针?价值观有什么问题?
您应该阅读更多关于智能指针的一般知识、它们是什么以及它们如何工作。
关于 unique_ptr
的简短版本是这样的:当发生以下两种情况之一时,包装对象被销毁并且内存被释放:
unique_ptr
本身被破坏(例如超出范围)
unique_ptr
被分配了另一个对象(通过 operator=
或 reset()
)。
在你的情况下,事情是这样发生的:
- 您正在创建一个对象并将其分配给构造函数中的
unique_ptr
- 您还在向量中存储指向该对象的原始指针
- 稍后,在您的按钮单击处理程序中,您创建一个新对象并将其分配给
unique_ptr
- 这会导致之前的对象被销毁
- 此时,您存储在 vector 中的指针是悬空的,它指向一个已销毁的对象
每次调用点击处理程序时都会重复这一系列步骤:您在 unique_tr
中创建并存储一个新对象,这会导致前一个对象被销毁,这意味着您的向量将只包含悬挂指针(最后一个除外)。
这些信息应该足以帮助您开始寻找解决方案。
我认为您困惑的核心是 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
的 "normal" 指针一样有效。
std::shared_ptr<>
添加一点(微小的)簿记,即存在的副本数(因为它 可以 被复制),并且只有在以下情况下才删除内存智能指针的 last 副本超出范围。
要么依赖你不存储(和使用)它们包含的哑指针.
在发现我需要 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;
但这不会破坏智能指针的目的吗?
真的很困惑。感谢您的宝贵时间。
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
可能是解决方案。
为什么你需要指针?价值观有什么问题?
您应该阅读更多关于智能指针的一般知识、它们是什么以及它们如何工作。
关于 unique_ptr
的简短版本是这样的:当发生以下两种情况之一时,包装对象被销毁并且内存被释放:
unique_ptr
本身被破坏(例如超出范围)unique_ptr
被分配了另一个对象(通过operator=
或reset()
)。
在你的情况下,事情是这样发生的:
- 您正在创建一个对象并将其分配给构造函数中的
unique_ptr
- 您还在向量中存储指向该对象的原始指针
- 稍后,在您的按钮单击处理程序中,您创建一个新对象并将其分配给
unique_ptr
- 这会导致之前的对象被销毁
- 此时,您存储在 vector 中的指针是悬空的,它指向一个已销毁的对象
每次调用点击处理程序时都会重复这一系列步骤:您在 unique_tr
中创建并存储一个新对象,这会导致前一个对象被销毁,这意味着您的向量将只包含悬挂指针(最后一个除外)。
这些信息应该足以帮助您开始寻找解决方案。
我认为您困惑的核心是 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
的 "normal" 指针一样有效。
std::shared_ptr<>
添加一点(微小的)簿记,即存在的副本数(因为它 可以 被复制),并且只有在以下情况下才删除内存智能指针的 last 副本超出范围。
要么依赖你不存储(和使用)它们包含的哑指针.