2015-10-13 40 views
1

下面的代碼導致內存崩潰導致崩潰。我假設這是因爲delete pTestStateMachine試圖刪除未在堆中分配的內存。那是對的嗎?添加到QStateMachine的QStates的內存管理

如果是這樣,是否暗示QStateMachine::addState(QAbstractState * state)必須始終傳遞一個動態分配的內存?不幸的是Qt docs doesen't沒有指定任何這樣的條件。我在這裏錯過了什麼?

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(); 
} 

回答

5

是否意味QStateMachine::addState(QAbstractState * state)必須始終通過一個動態分配的內存?

根本不是。 QState在任何方面都不是特別的,同樣的注意事項適用於任何QObject。回想一下,QObject是其他QObject s的容器:它擁有它們,除非它們先被單獨銷燬,否則將嘗試delete子對象QObject::~QObject

您的代碼可以通過多種方式修復 - 在任何情況下,目標都不會讓~QObject刪除不應該刪除的子狀態。

如果讓編譯器完成它應該做的工作,這一切都變得非常簡單。你使用原始擁有指針的代碼風格,而不是在定義時初始化它們,這是非慣用的,通常會激發你遇到的錯誤。如果您擁有指針,請使用std::unique_ptrQScopedPointerdelete和手動內存管理僅屬於單一目的的資源管理類。它根本不屬於通用代碼:將每個明確的delete都視爲一個錯誤。你不需要它們。

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) 
} 
+0

根據您的回答,我明白,如果應用程序刪除'QObject'說'QObjChild',它的父之前(說'QObjParent')超出範圍。然後,'QObjParent'的銷燬非常聰明,可以確定它的子QObjChild已經被破壞,並且不會執行任何破壞。那是對的嗎 ? –

+0

但是我在這裏看到的一個陷阱是,如果應用程序依賴於'QObjParent'來刪除它的子QObject,那麼所有的父母都會'刪除pChildQObject',如果沒有動態分配pChildQObject,那將是一個問題。理想情況下,在添加子對象時註冊刪除者不是很好嗎 –

+0

@Vivek如果子對象'QObject'沒有被動態分配,那麼確保父對象不在孩子身邊。這很簡單 - 你總是在控制之中,你只需要做正確的事情。 「在添加子對象時註冊刪除器不太好 - 」我不知道這意味着什麼。什麼刪除者?註冊在哪裏? ?? 「 –

2

從文檔

如果狀態已經在不同的機器上的措辭,將首先從它的舊機器中取出,然後加入到本機。

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(); 
}