Non vous ne rêvez pas, j’ai bien utilisé le mot magie dans le titre d’un article sur le blog On ne fait pas de la magie ! Mais vous vous doutez bien qu’il y a anguille sous roche et qu’on va faire de l’anti-magie.
L’une des opinions courantes sur le C++ est que c’est un langage difficile à employer, car il est facile de commettre des erreurs de programmation qui se traduisent par des fuites mémoires ou l’accès à des objets qui n’existent plus. En contrepartie, il est souvent également admis que le code C++ est plus rapide à l’exécution que du code Java ou C#, pour lesquels les fuites mémoires n’existent pas.
Quelles sont les raisons sous-jacentes de ces opinions ? La principale est que le développeur C++ doit gérer lui même la durée de vie des objets qu’il utilise. Cela se traduit souvent par la contrainte d’avoir à écrire un constructeur copie ou à surcharger l’opérateur d’affectation d’une classe. Mais cela lui offre la souplesse de maitriser l’instant exact où une ressource est libérée, qu’elle soit une ressource mémoire ou une connexion à une base de données.
Notamment, cela permet d’écrire une application en utilisant le principe RAII (Resource Acquisition Is Initialisation) sans avoir à veiller à chaque utilisation de sa classe à l’encapsuler avec une instruction using ou à ne pas oublier une clause finally d’un bloc try. C++ ne dispose pas de using ni de finally parce qu’il n’en a tout simplement pas besoin …
De ce fait, le développeur a moins d’instructions à écrire et l’application moins de code à exécuter : il n’y a pas de besoin de code supplémentaire exécuté dans un thread séparé pour faire le ménage dans les objets à détruire. Cerise sur le gâteau, les performances d’une application C++ sont déterministes, alors qu’une application Java ou C# peut présenter des ralentissements aléatoires déclenchées par le passage du garbage collector.
Ces opinions risquent de se renforcer à juste titre avec l’utilisation judicieuse des nouvelles fonctionnalités du C++ 11, maintenant largement adopté par les principaux compilateurs actuels. En effet, les nouveaux types de smart pointers, comme unique_ptr ou shared_ptr, ou la notion de RValues, qui permet d’écrire un constructeur move vont continuer à faciliter l’écriture d’application plus efficaces, en réduisant le nombre de lignes de code source écrites par le développeur, ainsi que le nombre d’instructions machine exécutées.
Le constructeur move est particulièrement efficace de ce point de vue, en évitant la création d’objets temporaires qui peuvent nécessiter des tailles mémoire importantes et des temps d’initialisation conséquents.
Pour obtenir ces résultats, qui ne sont pas de la magie, mais l’utilisation de mécanismes très bien pensés, le développeur C++ doit consacrer un temps d’apprentissage pour maitriser ces nouvelles notions. C’est le message que, pour ma part, je fais passer dans les formations C++, notamment la formation aux nouveautés C++ 11, que j’anime.