局部對象的析構順序與其創建順序的相反

當一個 QObject 對象在堆上創建的時候,Qt 會同時爲其創建一個對象樹。不過,對象樹中對象的順序是沒有定義的。這意味着,銷燬這些對象的順序也是未定義的。Qt 保證的是,任何對象樹中的 QObject 對象 delete 的時候,如果這個對象有 parent,則自動將其從 parent 的 children() 列表中刪除;如果有孩子,則自動 delete 每一個孩子。Qt 保證沒有 QObject 會被 delete 兩次,這是由析構順序決定的。


 

如果 QObject 在棧上創建,Qt 保持同樣的行爲。正常情況下,這也不會發生什麼問題。來看下下面的代碼片段:

  1. {
  2.     QWidget window;
  3.     QPushButton quit("Quit", &window);
  4. }

複製代碼

作爲父組件的 window 和作爲子組件的 quit 都是 QObject 的子類(事實上,它們都是 QWidget 的子類,而 QWidget 是 QObject 的子類)。這段代碼是正確的,quit 的析構函數不會被調用兩次,因爲標準 C++ (ISO/IEC 14882:2003)要求,局部對象的析構順序應該按照其創建順序的相反過程。因此,這段代碼在超出作用域時,會先調用 quit 的析構函數,將其從父對象 window 的子對象列表中刪除,然後纔會再調用 window 的析構函數。


 

但是,如果我們使用下面的代碼:

  1. {
  2.     QPushButton quit("Quit");
  3.     QWidget window;
  4.  
  5.     quit.setParent(&window);
  6. }

複製代碼

情況又有所不同,析構順序就有了問題。我們看到,在上面的代碼中,作爲父對象的 window 會首先被析構,因爲它是最後一個創建的對象。在析構過程中,它會調用子對象列表中每一個對象的析構函數,也就是說, quit 此時就被析構了。然後,代碼繼續執行,在 window 析構之後,quit 也會被析構,因爲 quit 也是一個局部變量,在超出作用域的時候當然也需要析構。但是,這時候已經是第二次調用 quit 的析構函數了,C++ 不允許調用兩次析構函數,因此,程序退出時崩潰了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章