2012-05-01 13 views
3

私はQLayoutオブジェクトがそのウィジェットの所有権を引き継いでいることをQtのドキュメントで知っています。しかし、QLayoutオブジェクトに関する限り、それをスタック上に作成してからsetLayout関数を使用してウィジェットに渡すことは安全ですか?または、ヒープ上に作成する必要がありますか?スタックにQLayoutを作成しても安全ですか?

#include <iostream> 

#include <QtGui/QApplication> 
#include <QPushButton> 
#include <QVBoxLayout> 

class LoudPushButton : public QPushButton 
{ 
public: 
    virtual ~LoudPushButton(){std::cout << "~LoudPushButton()" << std::endl;} 
}; 

class LoudQVBoxLayout : public QVBoxLayout 
{ 
public: 
    virtual ~LoudQVBoxLayout(){std::cout << "~LoudQVBoxLayout()" << std::endl;} 
}; 

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 

    QWidget window; 

    // On the heap 
    LoudQVBoxLayout* mainlayout = new LoudQVBoxLayout; 
    mainlayout->addWidget(new LoudPushButton); 
    mainlayout->addWidget(new LoudPushButton); 
    window.setLayout(mainlayout); 
    /* 
    // On the stack 
    LoudQVBoxLayout mainlayout; 
    mainlayout.addWidget(new LoudPushButton); 
    mainlayout.addWidget(new LoudPushButton); 
    window.setLayout(&mainlayout); 
    */ 
    window.show(); 

    return a.exec(); 
} 

両方の選択肢//スタック上と//​​ヒープ上の出口で同じ結果を生成:

~LoudQVBoxLayout() 
~LoudPushButton() 
~LoudPushButton() 

しかし、私は、これは未定義の動作ではないことを確認することができますか? windowdeleteというレイアウトで呼び出されますか?

EDIT:

LoudPushButton button; 
mainlayout->addWidget(&button); 
mainlayout->addWidget(new LoudPushButton); 

button*mainlayoutを同時に削除することが保証されている場合でも、未定義の動作を生成します:キャットプラスプラスすることによって答えを考慮して

私は推測。これは本当ですか?

+0

私は受け入れられた答えを再考すべきだと思います。私はCat Plus Plusの答えがQtのドキュメンテーションとどのように矛盾しているかを詳しく説明して私の投稿を編集しました。 – cgmb

答えて

1

Qtでは、QWidgetsをスタック上に構築できるようにオブジェクトツリーが設計されています。親が子供の前に作られている限り、彼らは正しく破壊されます。どちらの例も未定義の動作ではありません。

Qtのドキュメントにもgives an example、それがスタック上に両親とウィジェットを構築するために正当だ理由を説明:

int main() 
{ 
    QWidget window; 
    QPushButton quit("Quit", &window); 
    ... 
} 

このコードは正しいです:C++言語の標準的なので辞めのデストラクタは二回呼び出されません(ISO/IEC 14882:2003)は、ローカルオブジェクトのデストラクタがコンストラクタの逆の順序で呼び出されるように指定しています。したがって、子のデストラクタは終了し、最初に呼び出され、ウィンドウのデストラクタが呼び出される前に親のウィンドウから自身を削除します。

レイアウトは、いつでも破棄するように設計されているため、適切に動作する必要があります。 QWidget::setLayoutdocumentation言及:

をすでにこのウィジェットにインストールされているレイアウトマネージャが存在する場合、QWidgetのは、あなたが別のものをインストールさせません。新しいレイアウトでsetLayout()を呼び出す前に、まず既存のレイアウトマネージャ(layout()によって返される)を削除する必要があります。

Qtレイアウトシステムは、QWidgetsで設定されたQLayoutオブジェクトの有効期間を追跡し、このドキュメントが示すように適切に破壊を処理します。 QLayoutのデストラクタには、それが設定されているQWidgetから登録を解除するコードが含まれています。

+0

この詳細なポストをありがとうが、私はあなたの説明にポイントを追加しなければならないと思う:この特定の例ではスタックを使用するのが安全ですが、一般的にQtを使用すると非常に危険です。子と親の構造が反転されています。これは大規模な関数で簡単に発生する可能性があるので、ヒープを使用してそのような微妙なものからきれいにしておくことをお勧めします – Martin

4

すべてQObjectは、その子を削除します。親を持たないオブジェクトだけが自動ストレージを持つことができます。そしてQWidget::setLayoutはレイアウトを改めます。だから、いいえ、あなたはQLayoutでそれをすることはできません。

+0

ありがとう!私の編集が本当であるかどうか確認することができますか? –

+1

ドキュメントには、「QWidgetはレイアウトの所有権を得ます」と記載されています。削除の責任を負うスタックと、削除の責任を負うウィジェットの両方を持つことはできません。あなたが詳細を解き、この実装で回避策を見つけたとしても(コードが今書かれている方法の奇妙な理由のために)、あなたは契約に従うべきです... – HostileFork

+1

@MartinDrozdik:あなたがそれに追加するレイアウトの親ウィジェット同じ規則が適用されます。自動ストレージオブジェクトへのポインタで 'delete'を呼び出すのは、いつ起こるかにかかわらず、UBです。 –

関連する問題