如何从 C++ 中删除的对象中清除内存?
how to clear memory from deleted object in c++?
我想用 C++ 开发自己的信号范围。所以我为此目的使用qt。
我在 ui 中添加了一个 graphicsView 和一个按钮。在按下按钮的连接方法中,我更新了 graphicsView(最后我将把这个方法传递给一个线程)。
每次我按下按钮时,尽管指针被删除,内存的使用却在增加。我应该如何控制它?
我在 vs15 诊断工具中检查内存使用情况。
c++ 头文件:
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication.h"
#include <QGraphicsScene>
#include <QGraphicsPathItem>
class QtGuiApplication : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplicationClass ui;
QGraphicsScene* scene = new QGraphicsScene();
QPolygon pol;
QGraphicsPathItem* pathItem = new QGraphicsPathItem();
int index_ = 0; // graph offset
QPen* myPen = new QPen(Qt::red, 2);
private slots:
void btn_Display_clicked();
};
c++源文件:
#include "QtGuiApplication.h"
#include <math.h> /* sin */
#define pi 3.14159265
QtGuiApplication::QtGuiApplication(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(scene);
ui.graphicsView->show();
connect(ui.btn_Display, SIGNAL(clicked()), this, SLOT(btn_Display_clicked()));
}
void QtGuiApplication::btn_Display_clicked()
{
scene->removeItem(pathItem);
QPoint pos;
double x_amp_, y_amp_;
int x_pix_, y_pix_;
double phi_ = 0;
double freq_ = 5;
for (int i = 0; i<800; i++)
{
y_amp_ = 100 * sin(2 * pi*freq_*i / 811 + phi_) + 20 * index_;
x_amp_ = i;
x_pix_ = x_amp_;
y_pix_ = y_amp_;
pos.setX(x_pix_);
pos.setY(y_pix_);
pol.append(pos);
}
QPainterPath* myPath = new QPainterPath();
(*myPath).addPolygon(pol);
pathItem = scene->addPath(*myPath, *myPen);
delete myPath;
pol.clear();
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
在
pathItem = scene->addPath(*myPath, *myPen);
创建了一个新的 QGraphicsPathItem
并返回了一个指向 pathItem
的指针。迟早会再次单击该按钮
scene->removeItem(pathItem);
从场景中删除 QGraphicsPathItem
。很遗憾according to the documentation
The ownership of item is passed on to the caller (i.e., QGraphicsScene will no longer delete item when destroyed).
删除pathItem
由程序员决定。 pathItem
未删除并在随后调用
时泄露
pathItem = scene->addPath(*myPath, *myPen);
解决方法:在 pathItem
泄露之前将其删除。
scene->removeItem(pathItem);
delete pathItem;
应该完成这项工作。
我有几个意见。
您正在做很多完全没有必要的手动内存管理。为什么不好?因为如果你不这样做,你的错误就不可能通过构造来实现。您对手动内存分配(显式 new
)的所有使用都是不必要的。按值保存对象:它们被设计为以这种方式工作,让编译器担心其余部分。
您在过大的范围内声明对象。您在 class 中声明了多边形和笔,即使它们属于 btn_Display_clicked
的范围;您在循环外声明循环变量(x_amp_
和 y_amp_
)。
connect
语句是多余的:setupUi
为您建立连接。
您没有充分利用 C++11。
形式的包含 <QtModule/QClass>
将项目配置错误的检测推迟到 link 时间并且是不必要的。您应该直接包含 <QClass>
。如果你遇到编译错误,那么你的项目配置错误 - 也许你需要 re-run qmake(典型解决方案)。
在 C++ 中,<cmath>
是惯用的,而不是 <math.h>
。保留后者是为了与 C.
兼容
也许您可能希望使用 M_PI
。它是 C++ 中广泛使用的圆周率名称。
预分配多边形避免了在点存储增长时重新分配点存储的过早悲观。
使用 integer-based 多边形会降低精度,除非这是您出于特定原因做出的选择。否则,您应该使用 QPolygonF
.
这样界面就变成了:
#pragma once
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsPathItem>
#include "ui_QtGuiApplication.h"
class GuiApplication : public QMainWindow
{
Q_OBJECT
public:
GuiApplication(QWidget *parent = {});
private:
Ui::GuiApplicationClass ui;
QGraphicsScene scene;
QGraphicsPathItem pathItem; // the order matters: must be declared after the scene
int index_ = 0;
Q_SLOT void btn_Display_clicked();
};
和实施:
#include "GuiApplication.h"
#define _USE_MATH_DEFINES
#include <cmath>
#if !defined(M_PI)
#define M_PI (3.14159265358979323846)
#endif
GuiApplication::GuiApplication(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(&scene);
ui.graphicsView->show();
pathItem.setPen({Qt::red, 2});
scene.addItem(&pathItem);
}
void GuiApplication::btn_Display_clicked()
{
const double phi_ = 0;
const double freq_ = 5;
const int N = 800;
QPolygonF pol{N};
for (int i = 0; i < pol.size(); ++i) {
qreal x = i;
qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
pol[i] = {x, y};
}
QPainterPath path;
path.addPolygon(pol);
pathItem.setPath(path);
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
在 C++14 中,如果你想在别处重用它,你可以很容易地分解出生成器;然后循环将被替换为:
auto generator = [=,i=-1]() mutable {
++i;
return QPointF{qreal(i), 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_};
};
std::generate(pol.begin(), pol.end(), generator);
唉,就目前而言,btn_Display_clicked
实现丢弃了分配用于存储多边形的堆,并且它不必要地将数据从多边形复制到路径中的内部元素存储。我们可以通过直接修改 painter 项目的路径来避免这两种情况:
void GuiApplication::btn_Display_clicked()
{
const double phi_ = 0;
const double freq_ = 5;
const int N = 800;
if (pathItem.path().isEmpty()) { // preallocate the path
QPainterPath path;
path.addPolygon(QPolygon{N});
pathItem.setPath(path);
}
auto path = pathItem.path();
pathItem.setPath({}); // we own the path now - item's path is detached
Q_ASSERT(path.elementCount() == N);
for (int i = 0; i < path.elementCount(); ++i) {
qreal x = i;
qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
path.setElementPositionAt(i, x, y);
}
pathItem.setPath(path);
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
因为QPainterPath
是隐式共享值class,所以path = item.path(); item.setPath({});
序列相当于把路径移出路径项。随后的 setPath(path)
将其移回原处,因为我们不会对本地副本做进一步更改。
我想用 C++ 开发自己的信号范围。所以我为此目的使用qt。 我在 ui 中添加了一个 graphicsView 和一个按钮。在按下按钮的连接方法中,我更新了 graphicsView(最后我将把这个方法传递给一个线程)。 每次我按下按钮时,尽管指针被删除,内存的使用却在增加。我应该如何控制它?
我在 vs15 诊断工具中检查内存使用情况。
c++ 头文件:
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication.h"
#include <QGraphicsScene>
#include <QGraphicsPathItem>
class QtGuiApplication : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplicationClass ui;
QGraphicsScene* scene = new QGraphicsScene();
QPolygon pol;
QGraphicsPathItem* pathItem = new QGraphicsPathItem();
int index_ = 0; // graph offset
QPen* myPen = new QPen(Qt::red, 2);
private slots:
void btn_Display_clicked();
};
c++源文件:
#include "QtGuiApplication.h"
#include <math.h> /* sin */
#define pi 3.14159265
QtGuiApplication::QtGuiApplication(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(scene);
ui.graphicsView->show();
connect(ui.btn_Display, SIGNAL(clicked()), this, SLOT(btn_Display_clicked()));
}
void QtGuiApplication::btn_Display_clicked()
{
scene->removeItem(pathItem);
QPoint pos;
double x_amp_, y_amp_;
int x_pix_, y_pix_;
double phi_ = 0;
double freq_ = 5;
for (int i = 0; i<800; i++)
{
y_amp_ = 100 * sin(2 * pi*freq_*i / 811 + phi_) + 20 * index_;
x_amp_ = i;
x_pix_ = x_amp_;
y_pix_ = y_amp_;
pos.setX(x_pix_);
pos.setY(y_pix_);
pol.append(pos);
}
QPainterPath* myPath = new QPainterPath();
(*myPath).addPolygon(pol);
pathItem = scene->addPath(*myPath, *myPen);
delete myPath;
pol.clear();
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
在
pathItem = scene->addPath(*myPath, *myPen);
创建了一个新的 QGraphicsPathItem
并返回了一个指向 pathItem
的指针。迟早会再次单击该按钮
scene->removeItem(pathItem);
从场景中删除 QGraphicsPathItem
。很遗憾according to the documentation
The ownership of item is passed on to the caller (i.e., QGraphicsScene will no longer delete item when destroyed).
删除pathItem
由程序员决定。 pathItem
未删除并在随后调用
pathItem = scene->addPath(*myPath, *myPen);
解决方法:在 pathItem
泄露之前将其删除。
scene->removeItem(pathItem);
delete pathItem;
应该完成这项工作。
我有几个意见。
您正在做很多完全没有必要的手动内存管理。为什么不好?因为如果你不这样做,你的错误就不可能通过构造来实现。您对手动内存分配(显式
new
)的所有使用都是不必要的。按值保存对象:它们被设计为以这种方式工作,让编译器担心其余部分。您在过大的范围内声明对象。您在 class 中声明了多边形和笔,即使它们属于
btn_Display_clicked
的范围;您在循环外声明循环变量(x_amp_
和y_amp_
)。connect
语句是多余的:setupUi
为您建立连接。您没有充分利用 C++11。
形式的包含
<QtModule/QClass>
将项目配置错误的检测推迟到 link 时间并且是不必要的。您应该直接包含<QClass>
。如果你遇到编译错误,那么你的项目配置错误 - 也许你需要 re-run qmake(典型解决方案)。在 C++ 中,
<cmath>
是惯用的,而不是<math.h>
。保留后者是为了与 C. 兼容
也许您可能希望使用
M_PI
。它是 C++ 中广泛使用的圆周率名称。预分配多边形避免了在点存储增长时重新分配点存储的过早悲观。
使用 integer-based 多边形会降低精度,除非这是您出于特定原因做出的选择。否则,您应该使用
QPolygonF
.
这样界面就变成了:
#pragma once
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsPathItem>
#include "ui_QtGuiApplication.h"
class GuiApplication : public QMainWindow
{
Q_OBJECT
public:
GuiApplication(QWidget *parent = {});
private:
Ui::GuiApplicationClass ui;
QGraphicsScene scene;
QGraphicsPathItem pathItem; // the order matters: must be declared after the scene
int index_ = 0;
Q_SLOT void btn_Display_clicked();
};
和实施:
#include "GuiApplication.h"
#define _USE_MATH_DEFINES
#include <cmath>
#if !defined(M_PI)
#define M_PI (3.14159265358979323846)
#endif
GuiApplication::GuiApplication(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(&scene);
ui.graphicsView->show();
pathItem.setPen({Qt::red, 2});
scene.addItem(&pathItem);
}
void GuiApplication::btn_Display_clicked()
{
const double phi_ = 0;
const double freq_ = 5;
const int N = 800;
QPolygonF pol{N};
for (int i = 0; i < pol.size(); ++i) {
qreal x = i;
qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
pol[i] = {x, y};
}
QPainterPath path;
path.addPolygon(pol);
pathItem.setPath(path);
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
在 C++14 中,如果你想在别处重用它,你可以很容易地分解出生成器;然后循环将被替换为:
auto generator = [=,i=-1]() mutable {
++i;
return QPointF{qreal(i), 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_};
};
std::generate(pol.begin(), pol.end(), generator);
唉,就目前而言,btn_Display_clicked
实现丢弃了分配用于存储多边形的堆,并且它不必要地将数据从多边形复制到路径中的内部元素存储。我们可以通过直接修改 painter 项目的路径来避免这两种情况:
void GuiApplication::btn_Display_clicked()
{
const double phi_ = 0;
const double freq_ = 5;
const int N = 800;
if (pathItem.path().isEmpty()) { // preallocate the path
QPainterPath path;
path.addPolygon(QPolygon{N});
pathItem.setPath(path);
}
auto path = pathItem.path();
pathItem.setPath({}); // we own the path now - item's path is detached
Q_ASSERT(path.elementCount() == N);
for (int i = 0; i < path.elementCount(); ++i) {
qreal x = i;
qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
path.setElementPositionAt(i, x, y);
}
pathItem.setPath(path);
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
因为QPainterPath
是隐式共享值class,所以path = item.path(); item.setPath({});
序列相当于把路径移出路径项。随后的 setPath(path)
将其移回原处,因为我们不会对本地副本做进一步更改。