如何从 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; 

应该完成这项工作。

我有几个意见。

  1. 您正在做很多完全没有必要的手动内存管理。为什么不好?因为如果你不这样做,你的错误就不可能通过构造来实现。您对手动内存分配(显式 new)的所有使用都是不必要的。按值保存对象:它们被设计为以这种方式工作,让编译器担心其余部分。

  2. 您在过大的范围内声明对象。您在 class 中声明了多边形和笔,即使它们属于 btn_Display_clicked 的范围;您在循环外声明循环变量(x_amp_y_amp_)。

  3. connect 语句是多余的:setupUi 为您建立连接。

  4. 您没有充分利用 C++11。

  5. 形式的包含 <QtModule/QClass> 将项目配置错误的检测推迟到 link 时间并且是不必要的。您应该直接包含 <QClass>。如果你遇到编译错误,那么你的项目配置错误 - 也许你需要 re-run qmake(典型解决方案)。

  6. 在 C++ 中,<cmath> 是惯用的,而不是 <math.h>。保留后者是为了与 C.

  7. 兼容
  8. 也许您可能希望使用 M_PI。它是 C++ 中广泛使用的圆周率名称。

  9. 预分配多边形避免了在点存储增长时重新分配点存储的过早悲观。

  10. 使用 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) 将其移回原处,因为我们不会对本地副本做进一步更改。