Devo sempre allocare QObject e le classi derivate all'heap?

0

Ero nel canale #Qt irc, e ho mostrato un piccolo frammento del mio codice in uno stile su cui faccio molto affidamento. Sembra così:

/* Get Reply from Server */
QPointer<QNetworkReply> reply;
{
    QEventLoop waitForFile; // A QObject 
    connect(&m_NetworkAccessManager, &QNetworkAccessManager::finished, &waitForFile, &QEventLoop::quit);
    reply = m_NetworkAccessManager.get(request);
    waitForFile.exec();
    disconnect(&m_NetworkAccessManager, &QNetworkAccessManager::finished, &waitForFile, &QEventLoop::quit);
}

Mi è stato detto che il codice era piuttosto strano, il che non mi sorprende. Tendo a fare molto affidamento sullo stack e sono molto attento a controllare il ciclo di vita con { e } - Fondamentalmente il mio principio di funzionamento è stato

Delete all objects ASAP using scope.

In questo caso, il QEventLoop non ha bisogno di esistere oltre questo ambito e la sua utilità è relativa solo a QPointer<QNetworkReply> reply; , che è un ulteriore vantaggio per la leggibilità.

In ogni caso, quel codice ha richiesto la dichiarazione:

You should always allocate qobjects on the heap

Abbiamo avuto un po 'di discussione su questo. Francamente sono rimasto scioccato, perché ho operato sotto l'idea che:

Fear QObject pointers, and only use them for very specific usecases such as:

  1. If a parameter is designed to be able to accept NULL values.
  2. If the object being passed to a function is large, and a reference can not be used.
  3. If the object will be added to a QList.
  4. If you are developing a GUI, where parent/child relationships are paramount
  5. If you are working with special types, like QFile or QThread, where copying instances of these objects is conceptually null. (You wouldnt want two QFile's of the same file.)

Dopo aver dato le mie ragioni, si sono impegnate abbastanza duramente a lavorare secondo il principio di Always allocate QObjects to the heap. . Penso che se tu potessi farlo in modo abbastanza sicuro usando lo stack, non c'è una buona ragione per cui non vuoi un QObject sullo heap, data la facilità con cui QObject Lifecycle è in confronto alla semplice esecuzione dei puntatori del millimetro.

Penso che l'unico contesto pertinente da menzionare sia che questi sviluppatori erano principalmente abituati allo sviluppo di GUI, dove utilizzo Qt quasi esclusivamente per applicazioni console, il che si traduce in requisiti di codifica molto lineari.

E nonostante tutto questo, non ho una buona risposta.

Sono corretti? Ci sono dei buoni motivi per NON mettere un QObject sull'heap, anche se è progettato per funzionare in sicurezza?

Grazie.

    
posta Akiva 13.12.2018 - 20:06
fonte

2 risposte

3

La motivazione per non mettere QObject s nello stack è il modello di proprietà Qt. Se un oggetto viene distrutto, tutti i suoi oggetti figli vengono automaticamente cancellati. Se uno di questi oggetti figlio è allocato nello stack anziché tramite new , accadrà qualcosa di brutto.

In un'applicazione GUI, il codice deve gestire rapidamente un evento e quindi tornare al ciclo degli eventi. Quindi non puoi tenere in vita un oggetto per un qualsiasi periodo di tempo in pila. Ecco perché i programmatori della GUI useranno invece il modello di proprietà di Qt. Ciò consente loro di legare la proprietà di un oggetto a qualche altro oggetto come una finestra o un'applicazione.

La regola in realtà dovrebbe essere che dovresti creare solo QObjects senza genitori sullo stack. Se il QObject ha un genitore, il suo genitore dovrebbe avere il controllo di distruggerlo, non lo stack. Se non ha un genitore, allora è giusto che lo stack sia responsabile della sua distruzione.

    
risposta data 15.12.2018 - 03:35
fonte
2

La risposta breve è dovresti assegnare i tuoi oggetti in base alle loro vite logiche . Quando si tratta di codice asincrono, questo sarà l'heap, dal momento che più funzioni devono accedere al valore. Non esiste una regola universale per QObjects vs not-QObjects.

È un'idea terribile aspettare sui loop di eventi locali per aggirare scrivendo codice asincrono. Sicuramente ucciderà le prestazioni delle applicazioni a thread singolo che potrebbero fare altro lavoro mentre aspetti la comunicazione. Puoi sfruttare il parallelismo anche in applicazioni a thread singolo utilizzando un modello non bloccante, quando rispondi opportunisticamente alle richieste di rete che possono essere completate in qualsiasi ordine.

Da Wiki di Qt :

[...] you should never ever block the event loop [...] With the event delivery stuck, widgets won't update themselves (QPaintEvent objects will sit in the queue), no further interaction with widgets is possible (for the same reason), timers won't fire and networking communications will slow down and stop. Moreover, many window managers will detect that your application is not handling events any more and tell the user that your application isn't responding. That's why is so important to quickly react to events and return to the event loop as soon as possible!

L'IO asincrono è molto più efficiente del blocco sincrono. Non appena proverai a parallelizzare quel codice, avrai dei problemi. Poiché async io richiede la divisione della logica in più funzioni (in C ++), è necessario allocare oggetti persistenti nell'heap. Questo non è un grosso problema. Non c'è motivo di temere il mucchio. La durata degli oggetti in entrambi i casi è la lunghezza della transazione di rete . Non confondere questo con la lunghezza dell'ambito di un blocco nelle righe di codice.

[...] these developers were mainly accustomed to GUI development, where as I use Qt almost exclusively for console applications, which results in very linear coding requirements.

Il codice di rete è simile al codice GUI in quanto deve rispondere a eventi asincroni dal mondo esterno. Qt fornisce un ciclo di eventi orientato all'applicazione per console QCoreApplication che dovresti usare per strutturare le tue applicazioni di rete. Il tuo download manager dovrebbe essere il suo oggetto con l'applicazione principale come genitore.

Infine, il credo di

Delete all objects ASAP using scope.

non ha molto senso. Non appena si effettua una seconda richiesta di rete, si paga il costo di avvio e di smantellamento di un QEventLoop inutilmente. Se devi fare molte richieste questo può sommarsi. Di solito è meglio riutilizzare un'istanza di un oggetto complesso se è possibile, ed è sempre possibile nel caso di un ciclo di eventi.

    
risposta data 14.12.2018 - 03:08
fonte

Leggi altre domande sui tag