
FAQ C++Consultez toutes les FAQ
Nombre d'auteurs : 34, nombre de questions : 368, derni�re mise � jour : 14 novembre 2021 Ajouter une question
Cette FAQ a �t� r�alis�e � partir des questions fr�quemment pos�es sur les forums de http://www.developpez.com et de l'exp�rience personnelle des auteurs.
Je tiens � souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne pr�tend pas non plus �tre compl�te. Si vous trouvez une erreur ou si vous souhaitez devenir r�dacteur, lisez ceci.
Sur ce, nous vous souhaitons une bonne lecture.
8.1. Les fonctions inline (8)
8.2. Optimisation (5)
- Comment passer correctement des param�tres � ma fonction ?
- Qu'est-ce que la surcharge ?
- Quel est l'�quivalent C++ des param�tres variables ?
- Quelles pr�cautions faut-il prendre avec les fonctions callback ?
- Pourquoi ne faut-il qu'un seul return par fonction ?
- O� dois-je d�clarer mes variables locales ?
Par d�faut les param�tres de fonctions sont pass�s par valeur, c'est-�-dire que c'est une copie du param�tre pass� qui est manipul�e par la fonction et non l'original. Cela peut para�tre anodin lorsqu'il s'agit de passer un type de base, mais cela devient vite p�nalisant lorsqu'il s'agit d'une instance de classe dont la copie peut s'av�rer co�teuse (par exemple un vector de string). Cela peut �galement �tre un probl�me si l'on souhaite passer en param�tre une classe qui n'est tout simplement pas copiable (par exemple un flux standard). Pour r�gler le probl�me, on utilise ainsi ce qu'on appelle le passage par r�f�rence ou par r�f�rence constante. En passant une r�f�rence, on s'assure que c'est l'objet initial qui est manipul� dans la fonction et donc qu'aucune recopie ind�sirable n'est effectu�e.
En passant une r�f�rence constante, on s'assure �galement que notre param�tre ne pourra pas �tre modifi� par la fonction. Une bonne habitude est donc de prendre tout param�tre non modifiable par r�f�rence constante, except� les types primitifs. D'autant plus que cela n'a strictement aucune autre cons�quence, ni au niveau de la fonction ni au niveau de l'appelant.
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <string> #include <vector> void FonctionPasOptimisee(std::vector<std::string> Tab) { // ... } void FonctionOptimisee(const std::vector<std::string>& Tab) { // ... } std::vector<std::string> v(5000, std::string(1000, '-')); // Tableau de 5000 cha�nes de 1000 caract�res FonctionPasOptimisee(v); // recopie inutilement nos 5 millions de caract�res FonctionOptimisee(v); // ne recopie rien du tout |
Attention � ne pas oublier le const si le param�tre n'est pas modifi� dans la fonction : cela permet en effet de passer ce que l'on appelle des temporaires non nomm�s.
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <string> #include <vector> void FonctionIncorrecte(std::string& s) { // ... } void FonctionCorrecte(const std::string& s) { // ... } std::string s1 = "salut"; std::string s2 = "hello"; FonctionIncorrecte(s1); // Ok FonctionCorrecte(s1); // Ok FonctionIncorrecte(s1 + s2); // Erreur : s1 + s2 est un temporaire FonctionCorrecte(s1 + s2); // Ok FonctionIncorrecte("bonjour"); // Erreur : "bonjour" est un temporaire FonctionCorrecte("bonjour"); // Ok |
Cette remarque vaut �galement pour les pointeurs :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | void FonctionIncorrecte(char* s) { // ... } void FonctionCorrecte(const char* s) { // ... } FonctionIncorrecte("bonjour"); // Erreur : "bonjour" est un temporaire FonctionCorrecte("bonjour"); // Ok |
La surcharge est un m�canisme qui permet d'utiliser le m�me nom pour une fonction mais en lui passant des param�tres de types diff�rents et/ou en nombre diff�rent. Le nom de la fonction et les types des param�tres constituent ce qu'on appelle la signature de la fonction.
Code c++ : | S�lectionner tout |
1 2 3 | int moyenne(int i1, int i2); float moyenne(float f1, float f2); //surcharge valide float moyenne(int i1, int i2); //surcharge non valide |
En C, il est possible de d�clarer une fonction acceptant un nombre de param�tres variables via � ... � (c'est ce qu'on appelle l'ellipse). L'exemple le plus connu est celui de la fonction d'affichage printf().
En C++ il est bien entendu toujours possible d'utiliser cette m�thode mais il y a mieux : le cha�nage d'appels. Les avantages sont multiples :
- Typage beaucoup plus fort.
- Pas besoin de manipuler des macros bizarro�des pour r�cup�rer les param�tres.
- Pas besoin d'indication suppl�mentaire pour marquer le nombre et le type des param�tres.
- Beaucoup plus simple � �crire, et plus flexible.
Cette m�thode est intensivement utilis�e par exemple pour manipuler les flux standards :
Code c++ : | S�lectionner tout |
1 2 3 | #include <iostream> std::cout << x << y << z; |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <vector> class Polygon { pulic : Polygon& Add(int x, int y) { Points_.push_back(Point(x, y)); return *this; } private : std::vector<Point> Points_; }; Polygon Poly; Poly.Add(1, 2) .Add(5, 8) .Add(15, 19) .Add(0, 54); |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <sstream> #include <string> class StringBuilder { pulic : template <typename T> StringBuilder& operator ()(const T& t) { std::ostringstream oss; oss << t; String_ += oss.str(); return *this; } private : std::string String_; }; StringBuilder s; s("salut j'ai ")(24)(" ans "); |
En C++, en g�n�ral, on �vite d'utiliser les fonctions callback au profit d'alternatives un peu plus � objet � (tel les foncteurs par exemple). Cependant, on est parfois oblig� d'y recourir, typiquement pour s'interfacer avec une autre biblioth�que �crite en C. Si tel est votre cas, il vous faut alors �tre prudent et veiller � ce que votre fonction callback C++ pass�e � la biblioth�que C ne l�ve pas d'exception, surtout si vous l'utilisez d�j� compil�e (dll). La raison est que les exceptions C++ ne sont pas support�e en C, et les cons�quences d'une exception lev�e dans votre callback C++ et remontant jusqu'au code C appelant peuvent �tre f�cheuses. Et d'une mani�re plus g�n�rale, les exceptions posent probl�me d�s qu'il s'agit de franchir les limites d'un module compil� (telle une dll), m�me entre differents modules d�velopp�s en C++ (ABI incompatibles).
On peut toutefois pr�ciser que sous Windows, un syst�me d'exceptions (Structured Exception Handling, ou SEH) est int�gr� au sein m�me du syst�me. Certains compilateurs l'exploitent, y compris en langage C (au moyen de mots cl�s sp�cifiques), ce qui permet � du code C d'�tre travers� sans probl�me par des exceptions lanc�es depuis un code C++, si celles-ci ont �t� �mises sous forme de SEH. Certains compilateurs s'appuient sur SEH pour impl�menter leurs exceptions C++ (c'est le cas de Visual C++ par exemple), les rendant ainsi compatibles avec n'importe quel autre code compil�. Consultez la documentation de votre compilateur pour plus de d�tails.
Parce qu'on vous a menti !
Cette pratique provient d'anciens langages de programmation, qui ne disposaient pas de m�canismes �l�gants pour la gestion de la m�moire.
Ainsi, on n'avait coutume d'�crire des choses qui ressemblent � �a :
Code c : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int f() { char * my_string = (char*) malloc(20 * sizeof(char) ); int result; /* d�but du traitement complexe */ if(error) { result = -1; goto end_f; } /* fin du traitement complexe */ :end_f free(my_string); return result; } |
Heureusement, C++ poss�de un m�canisme tr�s puissant, le destructeur.
Ainsi, le code pr�c�dent s'�crira :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 | int f() { std::string my_string; /* d�but du traitement complexe */ if(error) return -1; /* fin du traitement complexe, qui contient maintenant le return */ } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 | int f() { std::unique_ptr<Obj> local_object = std::make_unique<CMyClass>(); // reste de la fonction } |
Et pour les objets allou�s avec new[] ? C++ ne fournit pas en standard de moyen... sauf d'utiliser plut�t std::vector !
Et comme souvent, si ce n'est pas possible, Boost vient � la rescousse avec boost::scoped_array.
Et pour les objets dont l'allocation est faite par une fonction d'une biblioth�que, � lib�rer en appelant une autre fonction de cette biblioth�que ? C++ nous offre la possibilit� d'utiliser des scope_guard, qui appelleront automatiquement la fonction de lib�ration lors de leur destruction.
Il n'y a donc de nos jours plus aucune raison de se priver de la possibilit� d'utiliser plusieurs instructions return, lorsque cela apporte plus de clart� et de lisibilit� au code.
R�ponse courte�:
Justifications
Certains langages comme Pascal ou le C90 limitent les endroits o� on peut d�finir des variables, ce qui force parfois � le faire non seulement bien avant leur premi�re utilisation (en d�but de nouveaux blocs imbriqu�s, voire en d�but de fonction/proc�dure), mais avant m�me qu'il soit possible de les initialiser correctement.
De fait, les r�gles qualit� en vigueur dans ces langages demandent g�n�ralement d'initialiser ces variables aussit�t que possible pour ne pas les laisser dans un �tat al�atoire. En effet, cela �vite les erreurs difficilement reproductibles (d'une machine � l'autre) que l'on observerait avec�:
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | int f(void) { int i; int result; if (h(time()) { result = g(i); } // ... du code2 ... i = 42; // ... du code3 ... return result; } |
Nous sommes en pleine programmation d�fensive�: on �vite au programme de planter, et tant qu'� faire, on essaie de rendre les comportements erron�s reproductibles. Seulement, nous masquons les vrais probl�mes. Dans ce code, il ne faudrait pas pouvoir appeler g(i) tant que i n'est pas dans un �tat pertinent (ici, 42). Et il faudrait �galement ne pas oublier d'affecter le r�sultat.
C'est l� que le C++ et le C99 entrent en sc�ne. Ces deux langages laissent le choix quant � l'endroit o� l'on peut d�clarer et d�finir une variable locale�: on peut le faire n'importe o� dans le corps d'un traitement (i.e. une fonction ou plus g�n�ralement un bloc d'accolades).
Cette propri�t� va nous permettre de d�l�guer au compilateur la v�rification des invariants de nos variables. Eh oui, aussi �trange que cela puisse para�tre, les variables ont bien des invariants�: �tre dans des �tats exploitables et pertinents. Dans un monde id�al, une variable respecte son invariant de pertinence ou� n'existe pas. Et l� est la r�ponse�: tant que l'on n'est pas capable de donner une valeur pertinente � une variable pour qu'elle soit exploitable, faisons en sorte que la variable n'existe pas.
Ainsi, notre code pr�c�dent deviendrait�:
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 | int f(void) { int result = valeur_par_defaut; if (h(time()) { result = g(i); } // ... du code2 ... const int i = 42; // ... du code3 ... return result; } |
Application aux constructeurs
Dans la continuit� de ce sujet, on retrouve les constructeurs. Leur r�le est de faire en sorte que les objets construits soient dans un �tat pertinent et exploitable � l'issue de la construction, ou de faire en sorte que ces objets n'existent pas. C'est pour cela que les fonctions init() sont d�cri�es dans les langages OO. En effet, on commence par avoir un objet partiellement construit jusqu'� l'appel de la fonction init(). Si l'appel � cette derni�re est oubli�, l'objet ne sera jamais compl�tement construit. Et si init() �choue, nous avons toujours un objet pr�sent, mais qu'il ne faut en aucun cas utiliser. Le code utilisateur devra le tester pour d�router � la main, et le code appel� devra tester dans toutes les fonctions si l'objet est bien initialis� pour bloquer la suite des op�rations.
Avoir un constructeur qui �choue (par exception) simplifie les choses�: par construction/�criture du code, si l'objet n'est pas exploitable, il ne peut pas exister et il n'y aura rien � tester.
N.B.�: des setters sont encore pires qu'init() � ce sujet. Quand on �crit trivialement des setters, on ne positionne jamais de flag is_initialized. Pire, il faudrait savoir que les 12 setters (diff�rents) de la classe ont bien �t� appel�s avant de pouvoir positionner ce flag. init() nous assure au moins de pouvoir v�rifier dynamiquement l'invariant � d�faut de pouvoir le faire statiquement (� la compilation).
Allons plus loin et rendons nos variables immuables
Sur un sujet connexe, si en plus nous savons que l'�tat de la variable ne changera jamais, il est pr�f�rable de la signaler comme non modifiable. Ce n'est pas pour des questions d'optimisation (car cela ne changera rien), mais pour des questions de facilit� de maintenance. Dans six mois, lors de la relecture de votre code, le const vous permettra de savoir imm�diatement qu'une fois la valeur connue, il n'est plus n�cessaire de r�fl�chir � ��Est-ce parce qu'elle a chang� que j'ai un bug�? Mais o� a-t-elle chang�? �
Voir � ce sujet�:
Summary:
const is your friend: Immutable values are easier to understand, track, and reason about, so prefer constants over variables wherever it is sensible and make const your default choice when you define a value: It's safe, it's checked at compile time (see Item 14), and it's integrated with C++'s type system. Don't cast away const except to call a const-incorrect function (see Item 94).
------
const est votre ami�: les valeurs immuables sont plus faciles � comprendre, pister et appr�hender, donc pr�f�rez les constantes aux variables lorsque cela a du sens et utilisez const par d�faut lorsque vous d�finissez une valeur : c'est plus s�r car c'est v�rifi� � la compilation (voir partie 14) et il est int�gr� au syst�me de type C++. N'effectuez pas de cast supprimant un const sauf pour appeler une fonction const-incorrecte (voir partie 94).
Proposer une nouvelle r�ponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plut�t sur le forum de la rubrique pour �aLes 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 � 2025 Developpez 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.