Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt 5 : 5.9.1 - Qt Creator : 4.3.1 - Qt Installer : 2.0.3 - JOM : 1.1.2 - Qt Build suite : 1.7.0 - VS Qt 5 : 2.0.0

Fonction clear () et bonnes pratiques

Bonjour !

J'ai implémenté une fonction clear() (remise à zéro, remise dans un état initial...) dans de nombreuses classes perso. Quand c'est l'équivalent d'un appel au constructeur il y a au moins deux manières de faire :
class Classe1
{
public:
Classe1 () :
m_value (0)
{
}

void clear ()
{
*this = Classe1 ();
}

private :
int m_value;
};
//------------------------------------------------------------------------------
class Classe2
{
public:
Classe2 () : m_toto (new QString())
{
clear ();
}

void clear ()
{
m_value = 0;
}

private :
int m_value;
QString * m_toto;
};
J'ai trouvé la version1 *this = Classe1 (); dans certaines sources Qt, elle n'est pas utilisable systématiquement mais est élégante et exprime parfaitement ce qu'est le clear
La version 2 est utilisable tout le temps, même quand il y a des initialisations de membres pointeurs.

Un avis, une préférence, des conseils, des remarques pour choisir entre ces deux méthodes ?

Réponses

  • J'ai trouvé la version1 *this = Classe1 (); dans certaines sources Qt, elle n'est pas utilisable systématiquement mais est élégante et exprime parfaitement ce qu'est le clear
    celle là elle ne me plaît pas du tout car elle laisse supposer que le constructeur renvoi une valeur, ce qui n'est pas le cas. En fait il s'agit du remplacement d'une variable existante (appel du destructeur puis du constructeur). C'est une construction que je n'avais encore jamais vu (ou alors je ne l'avais pas remarqué). J'aurais même eu comme réflexe de dire que ce n'était pas correcte.

    Bref, je préfère la seconde méthode ;-)
  • Tiens au fait il est curieux que la 1) soit possible sans définition de operator=(const Classe1 & other)
  • Salut,
    Et la version 1 est surtout pas performante : appel du destructeur puis du constructeur juste pour mettre une variable à 0.
  • À la lecture de vos posts je m'aperçois que j'ai mal interprété la syntaxe *this = Classe1()...

    @PapaJaac oui effectivement cette syntaxe devrait faire appel à l'operateur d'affectation. Tu as un exemple dans la librairie Qt où cette syntaxe est employée sans définition de l'opérateur = ?

    @loupium en fait il n'y a pas appel spécifique du destructeur et du constructeur, il y a appel de la fonction/opérateur = qui logiquement fait le boulot successif du destructeur puis du constructeur.
  • @loupium : une seule variable à 0... oui, dans mon exemple. Il peut aussi y avoir beaucoup de membres concernés. Mais ma question ne porte pas vraiment sur la performance, plutôt sur le style et les bonnes pratiques ;)
    @edlm10 Je ne me souviens plus dans quelle classe de Qt j'ai vu cela, désolé. Mais je suis sûr que c'est dans Qt, c'est le seul code que je regarde en-dehors du mien !
    Je suis étonné que l'exemple que j'ai donné compile, et même qu'il fonctionne parfaitement (le test est vite fait avec quelques lignes de plus pour modifier m_value).
    Y aurait-il un operator= par défaut ?
  • Oui il y a bien un opérateur = par défaut (qui applique cet opérateur sur l'ensemble des membres de la classe). Donc dans ton cas si il n'y a pas de définition explicite c'est la définition implicite qui s'applique.

    Pour résumé dans:
    *this = Classe1()
    Classe1() créé un object Classe1 qui est une rvalue référence, et *this = appelle l'opérateur/fonction Classe1 & operator=(const Classe1 &&rhs) (2 &) si il existe, sinon Classe1 & operator=(const Classe1 &rhs) (1 seul &).

    C'est simple, non ? ;-)
  • 2 &... ! C'est simplissime, transparent, limpide, cristallin :))

  • Finalement (après x posts et x minutes de réflexion - le mec lent ;-))) je dirais que la 1ère syntaxe est parfaite et préférable. Encore faut-il bien comprendre ce qui se passe, ce qui n'est pas sûr ;-)).

    Un expert (@gbdivers ?) peut confirmer/commenter ?
  • 2 &... !
    Pourtant c'est essentiel niveau performance: ca permet de déplacer au lieu de copier les données, ce qui peut parfois être vitale. C'est certainement la plus grosse nouveauté de C++11.

    Maintenant je suis le 1er à ne pas encore l'utiliser car j'essaie déjà de comprendre les bases... ;-)
  • Classe1() créé un object Classe1
    Donc constructeur avec son impacte en terme de performance.
    Et je suis désolé mais ce n'est pas négligeable.
    Donc je n'utiliserais jamais la version1.
  • Classe1() créé un object Classe1 qui est une rvalue référence
    rvalue, pas rvalue reference.
    (par contre, le "Classe1 &&rhs" est une rvalue reference)
    Par contre, pas de const avec un &&, cela se propage (il y a un item dans "effective moderne C++", il faudra que je le retrouve)

    Il y a aussi une version avec swap

    Attention aux contraintes de ce genre de code, en particulier si le constructeur par défaut est lourd, si la copie n'est pas triviale, etc.
    En surtout a n'utiliser que sur des sémantiques de valeur, jamais sur des entités.
    Maintenant je suis le 1er à ne pas encore l'utiliser car j'essaie déjà de comprendre les bases...
    Si tu suis les "bonnes" pratiques et n'utilises plus de pointeurs nus tout moches, tu n'as plus besoin de définir la copy et la move, donc tu n'auras pas trop a les utiliser directement

    Par contre, "plus grosse nouveauté du C++11", c'est discutable :)

    @loupium
    Les classes de valeur de Qt sont généralement légère et les constructeurs aussi (création du d_ pointeur et initialisation de quelques variables)
    Mais effectivement, tout dépend ce que l'on construit (mais peut être que si on construit trop, il y a un problème de SRP)
  • Les classes de valeur de Qt sont généralement légère et les constructeurs aussi (création du d_ pointeur et initialisation de quelques variables)
    Le fait qu'elle utilise du d_ pointeur n'est pas en faveur de la légèreté.
    Par exemple, le simple fait de créer une classe QColor("red") dans un Widget::paintEvent peut rapidement avoir des conséquences dramatiques sur le frame rate d'une application.
    Et puis franchement, c'est pas vraiment la philosophie d'un clear().
  • September 2015 modifié
    C'est pas faux. Mais le problème n'est pas forcement le d pointer ou la syntaxe du clear. Créer un QColor va créer une allocation + construction, si tu crées un QColor dans paint, ça sera appelé effectivement très souvent. Idem pour le d ptr, le problème est surtout le COW. Un pointeur est un simple entier, c'est pas lourd à créer.

    Ici, la construction dans le clear ne va pas (normalement) provoquer d'allocation, puisque cela devrait utiliser la move. Mais c'est vrai que les classes Qt ne supporte pas la move, donc cela fera un copy via COW pour QColor.

    EDIT : remarque, je ne milite pas pour la première syntaxe. Elle peut devenir problématique si mal utilisée.

    (HS c'est pour troller le pointeur sur QString ? :) )
  • la move
    quoi être ???
  • move semantic, c++11
Connectez-vous ou Inscrivez-vous pour répondre.