2013-09-24 10 views
5

Quando si crea una GUI con C++ e Qt è possibile creare un'etichetta per esempio come questo:C++ - Perché creo questi widget sull'heap?

QLabel* label = new QLabel("Hey you!", centralWidgetParent); 

Questo crea l'oggetto sul mucchio e ci rimarrà fino a che non sia eliminarlo manualmente, o il genitore viene distrutta. La mia domanda ora è perché ho bisogno di un puntatore per questo? Perché non crearlo in pila?

//Create a member variable of Class MainWindow 
QLabel label; 

//Set parent to show it and give a text so the user can see it 
    QWidget* centralWidget = new QWidget(this); //Needed to add widgets to the window 
    this->setCentralWidget(centralWidget); 
    label.setParent(centralWidget); 
    label.setText("Haha"); 

Questo funziona bene, posso vedere l'etichetta e non è scomparso.

Usiamo i puntatori in C++ per far vivere qualcosa di più così possiamo usare un oggetto in vari ambiti. Ma quando creo una variabile membro, non rimarrà finché l'oggetto non viene distrutto?

Modifica: Forse non l'ho chiarito abbastanza. Questa è la classe MainWindow:

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 
    QLabel label; //First introduced here... 

public: 
    MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 
}; 

//Constructor 
MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QWidget* centralWidget = new QWidget(this); 
    this->setCentralWidget(centralWidget); 
    label.setParent(centralWidget); 
    label.setText("Haha"); 
} 

risposta

3

Perché è così che è stato progettato Qt. Mi rendo conto che non è una risposta molto soddisfacente, ma Qt è stato progettato semplicemente come "i widget sono creati in un mucchio e i genitori sono responsabili della cancellazione dei loro figli".

Realizza che le origini di Qt sono vecchie, più vecchie di quelle che consideriamo "C++ moderno".

+0

Hmm ... beh, questo ha almeno un senso. Grazie. – Davlog

8

Se lo label esce dall'ambito, verrà chiamato il distruttore (QLabel::~QLabel). Da docs:

Distrugge l'oggetto, eliminando tutti i relativi oggetti figlio.

Non è necessario creare l'oggetto sul mucchio - si potrebbe mettere l'oggetto in pila, ma allora avete bisogno di essere responsabile sulla durata dell'oggetto (l'una delle questioni più problematiche in merito all'allocazione di dati su l'heap è la domanda "chi e quando dovrebbe cancellare questi oggetti?", e in Qt viene gestito dalla gerarchia - ogni volta che si cancella il widget, tutti i widget figli verranno cancellati).

Perché il tuo programma funziona - non so - potrebbe non funzionare (label viene distrutto alla fine dell'ambito). Un altro problema è - come cambierete il testo dello label (da un certo spazio, ad esempio) se non avete un riferimento ad esso?

Modifica Ho appena visto che il tuo label è un membro di MainWindow. È perfetto avere un oggetto completo e non il puntatore agli oggetti come membro della classe, in quanto non verrà distrutto prima che lo sia MainWindow. Si prega di notare che se si crea un'istanza del MainWindow come questo:

MainWindow *w = new MainWindow(); 

label verrà creato sul mucchio.

+0

Beh, posso chiamare l'etichetta. (Qualche funzione) ovunque io voglia. È un membro della mia classe MainWindow, quindi posso usarlo in qualsiasi spazio, funzione pubblica o privata. – Davlog

3

L'allocazione di un widget come variabile locale in genere non è una buona idea, poiché di solito andrà fuori campo prima di essere utile in alcun modo; poiché QObject supporta il pattern di composizione tramite le sue relazioni "parent-child" (che sono ben integrate con i distruttori C++), di solito la cosa più semplice è solo per sfruttare tale funzionalità.

D'altra parte, si può renderlo un membro della vostra classe MainWindow, o, in generale allocare in alcun modo tale che esso ha una durata inferiore rispetto alla durata del suo genitore. In effetti, quando tale QLabel viene distrutto, cancella automaticamente dal suo genitore, evitando la doppia deallocazione. Ma spesso è più comodo allocare i widget sullo heap, registrandoli come figli della finestra corrente, poiché in genere non è necessario accedere a molti widget dopo averli creati (ad esempio etichette), quindi non è necessario ingombrare la tua classe con membri dati inutili. Basta fare new QLabel(this, ...) nel costruttore della finestra e il gioco è fatto.

Che cosa si dovrebbe non fare, invece, è quello di allocare i widget senza new se il loro ciclo di vita si allunga rispetto alla durata del loro genitore (ad esempio mettendoli in una variabile globale o statico) - facendo questo farà sì che il genitore per provare a delete sulla sua distruzione, che causerà un crash al meglio, la corruzione della memoria silenziosa nel peggiore dei casi. Questo può risolvere (cancellando manualmente i widget nel distruttore di classe), ma non riesco a immaginare uno scenario in cui una cosa del genere sarebbe utile.

1

Motivo principale - possibilità di creazione/rimozione dinamica di widget. QObject (e QWidget) che desing come classi che non potevano avere copy constructor, quindi non si poteva passare è in argomenti (per valore/riferimento). Quindi, usare i puntatori in tutti i casi rende il codice più semplice e chiaro.

4

Un'altra cosa da tenere a mente è che Qt utilizza il cosiddetto paradigma pimpl in cui i dati oggetto sono implicitamente condivisi e gestiti dietro la classe che istanzia realmente. Vedi qui: http://qt-project.org/wiki/Dpointer

La linea di fondo è che se si sta allineando sullo stack per evitare di usare l'heap, Qt ne sta solo tirando uno veloce e usando comunque l'heap.

Problemi correlati