Bonjour,
Suite à une bonne dose de reflexion, il a été décidé que notre application ferait bien d'utiliser des pointeurs intelligents, afin de garantir que les données seraient bien là tant qu'on en a besoin, et bien détruites quand on ne s'en sert plus. Ces données étant potentiellement volumineuses, on s'est également dit qu'il serait bien qu'elles soient partagées tant qu'on ne les modifie pas.
Les QSharedData, QSharedDataPointer, et QExplicitlySharedDataPointer semblent donc parfaitement adaptés à la tâche.
N'ayant jamais programmé avec ces outils, j'ai codé rapidement des classes de test, afin de regarder le mode de fonctionnement. Et j'ai été surpris, en constatant que les QSharedPointer semblent se passer non pas un unique objet, mais des copies de cet objet.
En gros, voici le test réalisé :
Une affectation "normale", en fournissant au premier pointeur intelligent le pointeur vers l'objet à gérer (qui hérite de QSharedData, évidemment), en pa.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 QSharedDataPointer<TestSharedData> pa(new TestSharedData(a, QString("Objet Partage"))); QSharedDataPointer<TestSharedData> pb = pa; QSharedDataPointer<TestSharedData> pc; pc = pa;
Puis une affectation via le constructeur par copie, sur pb.
Et enfin, une affectation via l'opérateur d'affectation, sur pc.
En théorie, la documentation Qt dit que le constructeur par copie du QSharedDataPointeur devrait augmenter le compte de références, et assigner le pointeur sur l'objet partagé, rien de plus :
Et la même documentation dit aussi que l'opérateur d'affectation décrémente le compteur sur l'ancien objet partagé (en l'occurence, aucun objet), puis remplace le pointeur vers l'ancien objet par le pointeur vers le nouvel objet, et incrémente le compteur sur le nouveau :QSharedDataPointer::QSharedDataPointer ( const QSharedDataPointer<T> & other )
Sets the d pointer of this to the d pointer in other and increments the reference count of the shared data object.
Ces documentations semblent logiques.QSharedDataPointer<T> & QSharedDataPointer::operator= ( const QSharedDataPointer<T> & other )
Sets the d pointer of this to the d pointer of other and increments the reference count of the shared data object. The reference count of the old shared data object of this is decremented. If the reference count of the old shared data object becomes 0, the old shared data object is deleted.
Sauf que, quand j'interroge l'adresse de l'objet partagé "détenu" par les pointeurs intelligents, je constate ceci :
pa, après son initialisation, pointe sur un objet "partagé" (par lui seul), qu'on appelera A.
pb, après son initialisation par copie de pa, pointe sur A. Très bien, ça. Sauf qu'après la même opération, si on regarde pa, lui, il pointe sur un objet B, qui est construit par copie de l'objet A.
pc, après lui avoir affecté le pointeur pa, pointe sur l'objet B (le nouvel objet partagé par pa, si vous avez suivi). Sauf que pa, lui, pointe sur un objet C, obtenu par copie de l'objet B...
La question que je me pose, évidemment, est de savoir pourquoi cette ]@[^\^[|##! de classe QSharedDataPointer recopie les données, au lieu de ne recopier QUE le pointeur.
Je précise que la méthode de l'objet TestSharedData qui m'affiche l'adresse à laquelle se trouve l'instance courante est une méthode const, qui ne devrait donc pas déclencher la méthode detach()...
Pour ceux que ça intéresse, le code est fourni en pièces jointes.
La méthode "lanceTests(QObject *)" est celle utilisée à partir du main, en fournissant la QApplication en paramètre.
Alors, petite précision :
En ajoutant des tests, il semblerait que les affectations (par constructeur de copie, ou par opérateur d'affectation) se passent effectivement comme prévu, mais que les déréférencements utilisés pour visionner l'adresse de l'objet partagé déclenchent la copie systématiquement.
Par exemple :
donne :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 QSharedDataPointer<TestSharedData> pd(new TestSharedData(a, QString("D"))); std::cout << "pd->x() = ##" << pd->x() << "##\n"; QSharedDataPointer<TestSharedData> pe = pd; QSharedDataPointer<TestSharedData> pf = pd; std::cout << "pd->x() = ##" << pd->x() << "##\n"; std::cout << "pe->x() = ##" << pe->x() << "##\n"; std::cout << "pf->x() = ##" << pf->x() << "##\n";
Code x : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 Constructeur de base appele : @@D@@ pd->x() = ##D[08054300]## Constructeur de copie appele : ##D## pd->x() = ##Copie de D[0805db38]## Constructeur de copie appele : ##D## pe->x() = ##Copie de D[0805dbd8]## pf->x() = ##D[08054300]##
Comme on peut le voir, c'est l'appel au déréférencement qui déclenche ça. (pas de copie le premier coup, puisque un seul pointeur sur l'objet partagé, puis on affecte l'objet à deux autres pointeurs, et on déréférence à nouveau. Cette fois, pd obtient une copie, laissant à pe et pf l'original. Puis on déréférence pe, qui obtient aussi une copie, laissant l'original à pf. Puis on déréférence pf, ce qui ne fait rien, car il est le dernier sur cette copie)
Mais... pourquoi n'utilise-t-il pas le déréférencement constant, étant donné que la méthode x() est constante aussi ?
Partager