2016-04-18 8 views
2

私はのWindows7上Qt5を使用していると私は最近an interesting Qt example codeを見つけました。Qt:このクラスのデストラクタを記述するための正しい、安全な方法は何ですか?

基本的には、次のようになります。

ButtonWidget::ButtonWidget(const QStringList &texts, QWidget * parent) 
: QWidget(parent) 
{ 
    signalMapper = new QSignalMapper(this); 

    QGridLayout * gridLayout = new QGridLayout; 
    for (int i = 0; i < texts.size(); ++i) 
    { 
     QPushButton * button = new QPushButton(texts[i]); 
     connect(button, SIGNAL(clicked()), signalMapper, SLOT(map())); 
     signalMapper->setMapping(button, texts[i]); 
     gridLayout->addWidget(button, i/3, i % 3); 
    } 

    connect(signalMapper, SIGNAL(mapped(QString)), this, SIGNAL(clicked(QString))); 

    setLayout(gridLayout); 
} 

それは素晴らしく、有用な例であるが、それは私がButtonWidget型のオブジェクトを削除したいだけの場合には...適切なデストラクタを持っていません、またはウィジェットを削除/追加できるようにコードをカスタマイズしたい場合コンストラクタで作成されたすべてのオブジェクトを削除する方法です(動的にはnewを使用)。

私のアプローチは、プライベート変数QList<QPushButton*> listを使用し、上記のlistを使用して、デストラクタで(コンストラクタ内に)リストするためにすべての新しい割り当てボタンを追加し、それらを1つずつ削除することでした。しかし、それは幼稚園に近いアプローチのようです。

私はリストなしで、あなたの時間と忍耐をありがとう:)コンストラクタコードをいじってなくて、他の方法、それを行うには良い方法がなければならないと思います!

+1

Qtのドキュメントを読んで、Qtが動的に割り当てられたオブジェクトをどのように管理するのかを理解する必要があります。 –

答えて

5

QSignalMapperなどのコンストラクタの引数としてparentを受け取るQtクラスでは、クラス自体が親のオブジェクトリストに追加され、その親(QObject)が破棄されると破壊されることに注意することが重要です。あなたがオブジェクトの親を渡す場合

したがって、あなたは何もする必要はありません。あなたのcppファイルに空のデストラクタがあるかもしれません。メンバの定義がQObjectで利用可能かどうかを確認するだけですが、それはクラスに依存します。あなたがあなた自身のデストラクタを書くことを選択しない場合

しかし、正しく実装されている典型的なQtの「子」は破壊でその親から自分自身を削除します。キャプション/タイトルに提起質問に加えて

は、OPは、彼が前に親を削除するサブオブジェクトを削除したい場合に何が起こるか尋ねた:

followingを目撃、それはそうです(この自分の経験である)、子ウィジェットを削除するだけで、自分自身を削除することができます。子の親をnullに設定することができると言われていますが、親を削除して子を生きたままにしたい場合を除いて、これは必ずしも必要ではありません。

exampleの最後の段落に示されているようしかし、インスタンス化の順序は重要です。親が子の後にインスタンス化され、子の親が明示的に設定されている場合は、親に死んで参照/無効なポインタは、子供が残されます、そして、それから自分自身を削除しようとすると未定義の動作は、子の破壊で発生しますスコープ外の親です。

Qtのdocumentationに加えて、QObject :: setParent here (setParent_helper)の実装を見ることもできます。これから、子供/両親の削除が事件を起こすことなく、上記の場合を除いて、彼らが大いに努力することが分かる。

+0

わかりました。しかし、単にコンストラクタで作成されたボタンを単に削除するメソッドが必要な場合はどうすればよいですか?将来、私は 'ButtonWidget'型のオブジェクト全体を破壊しないと仮定します。そして、私はボタンを削除する必要があり、新しいボタンを追加するための別の方法が必要だとしますか? –

+0

親を削除するか、ボタンを削除してください。私は正しかったが、うまくいくはずだ。 –

+0

あなたは自分で管理する必要があります。 'SomeQObject-> setParent(0)'を使うと、親からオブジェクトを切り離すことができます。その後、単に削除することができます。 – AndreasT

3

QWidget::setLayoutから:

QWidgetのレイアウトの所有権を取得します。(QLayout::addWidgetによって呼び出される)QLayout::addItemから

注:アイテムの所有権は、レイアウトに転送され、それはそれを削除するには、レイアウトの責任です。


あなたは何をクリーンアップする必要はありません。
addWidgetremoveWidget/removeItem)のレイアウトでウィジェットを管理します。

3

すべてのウィジェットをQtのobject treeに接続してください。そうすれば、あなたのウィジェットはあなたのために処理されます。あなたはそれを構築するときに親を与えることによってそれを行うことができます。 SignalMapperと同じように

1

「適切な」デストラクタがないことは間違いありません。 であり、デストラクタはコンパイラによって生成され、そのデストラクタはリソースを解放するために必要なすべてを行います。それがどうあるべきか、現代のC++コードをどのように設計すべきかということです。

適切に設計されたC++クラスは、リソースを明示的に管理することなく使用できる必要があります。それはここの場合です。

さらに、この例では、単純にクラスメンバーであったメンバーを不必要に動的に割り当てます。 C++ 11では、シグナル・マッパーも必要ありません。これは私がそれをする方法です:

class ButtonWidget : public QWidget { 
    Q_OBJECT 
    QGridLayout m_layout { this }; 
public: 
    ButtonWidget(const QStringList &items, QWidget *parent = 0); 
    ~ButtonWidget(); 
    Q_SIGNAL void buttonClicked(const QString &); 
} 

ButtonWidget::ButtonWidget(const QStringList &items, QWidget *parent) 
: QWidget(parent) 
{ 
    const int columns = 3; 
    for (int i = 0; i < items.size(); ++i) { 
    auto text = items[i]; 
    auto button = new QPushButton(text); 
    connect(button, &QPushButton::clicked, [this, text]{ 
     emit buttonClicked(text); 
    }); 
    m_layout.addWidget(button, i/columns, i % columns); 
    } 
} 

ButtonWidget::~ButtonWidget() {} 

これは、メモリリークのない完全な、使用可能なウィジェットです。これが現代のC++/Qtの見方です。デストラクタで何か面白いことをする必要がある場合は、メモリ管理をRAIIクラスに分解することを常に考慮する必要があります。たとえば、デストラクタでファイルハンドルを手動で閉じるのではなく、QFileの使用を検討するか、同様のリソース管理クラスを作成して、ハンドルのライフタイムを手動で管理することを心配することなく使用できます。

関連する問題