添加到qstatemmachine的QStates内存管理

Memory management of QStates added to QStateMachine

本文关键字:内存 管理 QStates qstatemmachine 添加      更新时间:2023-10-16

下面的代码由于内存损坏导致崩溃。我假设这是因为delete pTestStateMachine试图删除未在堆中分配的内存。对吗?

如果是,这是否意味着QStateMachine::addState(QAbstractState * state)必须总是传递一个动态分配的内存?不幸的是Qt文档没有指定任何这样的条件。我遗漏了什么?

class CTestClass
{
public:
    QState m_pTestState;
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QStateMachine *pTestStateMachine;
    CTestClass  TestClass;
    pTestStateMachine = new QStateMachine();
    pTestStateMachine->addState(&(TestClass.m_pTestState));
    pTestStateMachine->setInitialState(&(TestClass.m_pTestState));
    pTestStateMachine->start();
    pTestStateMachine->stop();
    delete pTestStateMachine;
    return a.exec();
}

是否意味着QStateMachine::addState(QAbstractState * state)必须总是传递一个动态分配的内存?

一点也不。QState在任何方面都不特殊,同样的警告适用于任何QObject。回想一下,QObject是其他QObject的容器:它拥有它们,除非它们首先被单独销毁,否则将尝试在QObject::~QObjectdelete子对象。

您的代码可以通过几种方式修复-在所有情况下,目标都是不让~QObject删除它不应该删除的子状态。

如果你让编译器做它应该做的工作,一切都会变得非常简单。您的代码风格是使用原始的拥有指针,而不是在定义点初始化它们,这是不习惯的,并且经常会引发您遇到的错误。如果有归属指针,则使用std::unique_ptrQScopedPointerdelete和手动内存管理只属于单一用途的资源管理类。它根本不属于通用代码:认为每个显式delete都是一个bug。你不需要他们。

class CTestClass
{
public:
  QState m_pTestState;
};
// Fix 1: Don't mix automatic storage duration with dynamic storage duration
int main(int argc, char *argv[])
{
  QCoreApplication a(argc, argv);
  {
    QStateMachine TestStateMachine;
    CTestClass    TestClass;
    TestStateMachine.addState(&TestClass.m_pTestState);
    TestStateMachine.setInitialState(&TestClass.m_pTestState);
    TestStateMachine.start();
    TestStateMachine.stop();
  } // <-- here the compiler emits
    // TestClass.~TestClass()
    // ...
    // TestStateMachine.~QStateMachine()
    //   ...
    //   TestStateMachine.~QObject()
}
// Fix 2: Make sure that the child doesn't outlive the parent.
int main(int argc, char *argv[])
{
  QCoreApplication a(argc, argv);
  {
    QScopedPointer<QStateMachine> TestStateMachine(new QStateMachine);
    CTestClass                    TestClass;
    TestStateMachine->addState(&TestClass.m_pTestState);
    TestStateMachine->setInitialState(&TestClass.m_pTestState);
    TestStateMachine->start();
    TestStateMachine->stop();
  } // <-- here the compiler emits
    // TestClass.~TestClass()
    // ...
    // TestStateMachine.~QScopedPointer()
    // delete data;
    //   data->~QStateMachine
    //   ...
    //   data->~QObject
    //   free(data)
}

从文档的措辞

如果状态已经在另一台机器上,它将首先从旧机器上删除,然后再添加到这台机器上。

QStateMachine获取QState的所有权这意味着它会在销毁时删除它拥有的所有状态,你可以传递一个动态分配的指针或者你可以使用QStateMachine::removeState()

从此状态机中删除给定的状态。状态机释放状态的所有权。

所以这应该可以工作:

class CTestClass
{
public:
    QState m_pTestState;
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QStateMachine *pTestStateMachine;
    CTestClass  TestClass;
    pTestStateMachine = new QStateMachine();
    pTestStateMachine->addState(&(TestClass.m_pTestState));
    pTestStateMachine->setInitialState(&(TestClass.m_pTestState));
    pTestStateMachine->start();
    pTestStateMachine->stop();
    pTestStateMachine->removeState(&(TestClass.m_pTestState)); //removing state before deletion
    delete pTestStateMachine;
    return a.exec();
}