Developpez.com

Plus de 14 000 cours et tutoriels en informatique professionnelle à consulter, à télécharger ou à visionner en vidéo.

FAQ Qt FAQ Qt Creator FAQ PyQt & PySide

FAQ QtConsultez toutes les FAQ

Nombre d'auteurs : 26, nombre de questions : 298, dernière mise à jour : 6 mai 2017 

 
OuvrirSommaireGénéralités techniquesMémoire

Qt est basé sur une sémantique de copie, c'est-à-dire qu'elle utilise beaucoup la copie pour permettre de créer des logiciels robustes et évite un bon nombre de problèmes. Pour optimiser cela, Qt implémente le pattern COW (Copy on Write) : la copie interne est réellement effectuée lors d'un accès en écriture sur un objet interne partagé d'une classe. Dit d'une autre manière, Qt va partager en lecture un objet interne entre différentes instances identiques d'une classe.

Le principe est le suivant :
  1. Lors d'une copie, un objet interne est partagé.
  2. Tant qu'aucune des instances ne modifie cet objet, il reste partagé.
  3. Dès qu'une des instances modifie l'objet, cette instance va copier l'objet interne et appliquer la modification.
 
Sélectionnez
// s1 va créer un objet interne contenant "hello word"
QString s1 = "hello word";

// s2 va référencer le même objet interne que s1.
// Il n'y a pas eu de vraie copie.
QString s2 = s1;

// Accès en lecture au premier caractère => inutile de faire une copie
qDebug()<< s2[0];

// On veut modifier la première lettre de s2 :
// s2 créé un nouvel objet interne et copie le contenu.
// s1 et s2 n'utilisent plus le même objet interne
// s2 modifie la première lettre
s2[0] = 'b';

Remarque : ce pattern est thread safe dans l'implémentation Qt.

Mis à jour le 7 mai 2012  par Yan Verdavaine

Lien : Liste des classes Qt basées sur le COW
Lien : Comment optimiser la copie de ses classes ?

Qt est conçu de façon à rendre aisée la gestion de la mémoire. Il y a deux mécanismes pour y arriver : les types simples et les hiérarchies.

Les objets simples tels que les chaînes de caractères, conteneurs… peuvent être manipulés comme les types de base, la mémoire est nettoyée automatiquement par le destructeur.

 
Sélectionnez
QString getSiteName()
{
    // pas de problème pour l'allouer sur la pile
    QString siteName = "Developpez.com";
    // pas de problème pour l'utiliser comme type de retour
    return siteName;
}

Ce type d'objet n'utilise en interne qu'un pointeur vers un délégué, ce qui veut dire que la manipulation de ces objets est extrêmement rapide.

 
Sélectionnez
QRectF rect(0.0, 0.0, 50.0, 50.0);
// le constructeur par copie est rapide
QRectF rectClone = rect;
QRectF copy;
// l'affectation est tout aussi rapide
copy = rect; 

Ce type d'objet est donc très facile à utiliser car on peut l'employer comme s'il s'agissait d'un type de base.

D'autres objets ont besoin d'une gestion plus complexe de la mémoire car il faut contrôler précisément leur durée de vie. Pour ces objets, Qt a introduit le mécanisme de hiérarchie d'objets, qui est disponible pour toutes les sous-classes de QObject.

Les objets d'une hiérarchie peuvent avoir un objet parent, et des objets enfants (à ne pas confondre avec l'héritage, il s'agit ici d'encapsulation). Lorsqu'un objet est détruit, tous ses objets enfants sont détruits aussi.

Le parent est précisé dans le constructeur des objets ou grâce à la méthode QObject::setParent().

 
Sélectionnez
QString giveString()
{
    QObject parent;
    MyObjet *myObjet = new MyObject(&parent);
    myObjet->doSomeComputation();
    return myObjet->getString();
}

Étonnamment, le code précédent n'a pas de fuite de mémoire. L'objet MyObjet, créé sur le tas, a, comme parent, le QObject "parent". Lorsque le parent arrive à la fin de la fonction giveString(), son destructeur se charge de supprimer l'objet myObjet.

L'exemple précédent est artificiel, voyons un exemple plus réaliste.

 
Sélectionnez
void showMessage()
{
    QDialog dialog;
    QVBoxLayout *layout = new QVBoxLayout(&dialog);
    QLabel *message = new QLabel("Cliquez sur le bouton");
    layout->addWidget(message);
    QButton *button = new QButton("Cliquez ici");
    layout->addWidget(button);
    dialog.exec();
}

Quelle magie fait que ce code fonctionne ? Lorsqu'un widget est ajouté au layout, le layout le reparente automatiquement avec son widget propriétaire (dialog dans ce cas-ci). Finalement, lorsque la fonction se termine, le destructeur de dialog supprime tous les objets et nettoie la mémoire.

Mis à jour le 7 mai 2012  par Benjamin Poulain

Lien : Comment optimiser la copie de ses classes ?

Une majeure partie de Qt est basée sur le COW. Il permet ainsi d'utiliser ce pattern. Pour cela, il faut créer une classe qui hérite de QSharedData, et qui possède un constructeur, un constructeur par copie et un destructeur public. Cette classe sera l'objet interne qui sera partagé. Elle possède un compteur de références thread safe et ne doit pas être directement accédée. Sa vie sera gérée par d'autres classes.

Pour accéder à une instance de cette classe, deux choix sont possibles :

  • QSharedDataPointer : permet de partager implicitement un QSharedData. L'objet interne est partagé en lecture. L'accès à l'objet en écriture va générer une copie de l'objet ;
  • QExplicitlySharedDataPointer : permet de partager explicitement un QSharedData. L'objet interne est partagé en lecture/écriture. L'objet interne sera copié uniquement sur demande.

Ces deux classes sont des pointeurs intelligents spécialisés dans la manipulation des pointeurs sur QSharedData. Ils implémentent donc la sémantique des pointeurs avec des accès const (lecture) et non const (écriture). Elles détruiront le QSharedData une fois son compteur à zéro. Ces pointeurs intelligents possèdent deux fonctions qu'il est utile de connaître :

  • detach() : si le compteur de référence est supérieur à 1, le QSharedData sera copié ;
  • reset() : initialise à null le pointeur intelligent.

Remarque : ces classes sont thread safe.

Mis à jour le 7 mai 2012  par Yan Verdavaine

Lien : Comment Qt optimise-t-il les copies ?
Lien : Faq C++ : pointeurs intelligentsFaq C++ : pointeurs intelligents
Lien : Tutoriel : Pointeurs intelligents

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 - 2017 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.