std::unique_ptr/shared_ptr,編譯期確定所有權,現代 C++ 首選。new/delete:靈活但易漏刪/重刪,需極強紀律。Qt 在 C++98 時代設計,未引入標準智能指針,構建了**基於對象樹的自動回收機制**,兼具「確定性銷毀」與「易用性」。
QObject 子類構造函數接受 QObject* parent。parent 析構時,遞歸 delete 所有子對象(調用 QObject::deleteChildren())。
QWidget *window = new QWidget;
QPushButton *btn = new QPushButton("Click", window); // window 為 parent
// window 刪除時,btn 自動刪除,無需手動管理
普通指針 delete 後變野指針;QPointer 監視對象生命週期,對象銷毀自動置 nullptr(實為 0)。
QDate *raw = new QDate(QDate::currentDate());
QPointer guarded = raw;
raw->year(); // 正常
guarded->year(); // 正常
delete raw;
// raw 仍指向已釋放內存(野指針)
// guarded.isNull() == true,安全判空
QObject 的類型。當對象**不在同一父子樹**但需統一生命週期管理時(如:多個獨立窗口隨某按鈕關閉)。
QObjectCleanupHandler *cleaner = new QObjectCleanupHandler;
QPushButton *btn1 = new QPushButton("Remove Me");
btn1->show();
cleaner->add(btn1);
connect(btn1, &QPushButton::clicked, btn1, &QObject::deleteLater);
QPushButton *btn2 = new QPushButton("Nothing");
btn2->show();
cleaner->add(btn2); // 無自銷毀邏輯
QPushButton *btn3 = new QPushButton("Remove All");
btn3->show();
cleaner->add(btn3);
connect(btn3, &QPushButton::clicked, cleaner, &QObject::deleteLater);
// 點擊 btn3 → cleaner deleteLater → 觸發 cleaner 析構 → 刪除 btn1/btn2/btn3
add() 註冊對象;對象自行 deleteLater() 時,cleaner 自動從列表移除。cleaner 自身 deleteLater() 會級聯刪除所有剩餘註冊對象。Qt 內部類(QSharedDataPointer、QSharedPointer)已提供成熟實現,此處僅作原理演示。
class CountedObject : public QObject {
Q_OBJECT
int ctr = 0;
public:
void attach(QObject *obj) {
++ctr;
connect(obj, &QObject::destroyed, this, &CountedObject::detach);
}
public slots:
void detach() {
if (--ctr <= 0) delete this;
}
};
缺陷:無法防止重複 attach 同一對象導致計數虛高。
class CountedObject : public QObject {
Q_OBJECT
QList owners;
public:
void attach(QObject *obj) {
if (!obj || owners.contains(obj)) return;
owners.append(obj);
connect(obj, &QObject::destroyed, this, [this, obj](){ detach(obj); });
}
public slots:
void detach(QObject *obj) {
owners.removeAll(obj);
if (owners.isEmpty()) delete this;
}
};
obj 避免 sender() 不可靠問題。operator new 強制註冊(極客向)全局重載 new,在分配時自動註冊到線程局部註冊表,配合 atexit 或線程結束清理。複雜度高,生產環境**強烈建議直接使用 QSharedPointer/std::shared_ptr**。
| 場景 | 推薦方案 |
|---|---|
| UI 組件、邏輯對象有明確父子關係 | 構造函數傳 parent,零代碼管理 |
| 需跨作用域觀察對象存活 | QPointer |
| 異構對象需綁定同一生命週期 | QObjectCleanupHandler |
| 共享所有權、非 QObject 類型 | std::shared_ptr / QSharedPointer |
| 獨占所有權、高性能 | std::unique_ptr / QScopedPointer (Qt5 棄用) |