IdentifiantMot de passe
Loading...
Mot de passe oubli� ?Je m'inscris ! (gratuit)
logo

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.

SommaireLes fonctions (19)
pr�c�dent sommaire suivant
 

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

Mis � jour le 17 octobre 2005 Laurent Gomila

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

Mis � jour le 20 avril 2003 Cline

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;
On peut �galement imaginer d'autres formes de cha�nages pour d'autres applications :

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 ");
Comme vous le voyez, ce qui rend possible le cha�nage est le renvoi de l'instance courante par la fonction. Ainsi, Poly.Add(x, y) renvoie Poly, sur lequel on peut de nouveau appeler Add() etc.

Mis � jour le 17 octobre 2005 Laurent Gomila

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.

Mis � jour le 15 octobre 2009 Aurelien.Regat-Barrel

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; 
}
Ceci permettait de g�rer toute la lib�ration m�moire dans un seul bloc de code. Mais pour passer dans ce bloc de code � coup s�r, il �tait n�cessaire d'utiliser, soit des imbrications de if � n'en plus finir, soit des gotos. Les return multiples �taient sources de nombreuses fuites m�moires, difficiles � diagnostiquer.
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 */ 
}
Plus besoin de g�rer l'allocation m�moire, elle sera lib�r�e par le destructeur de std::string. Et si l'on est oblig� d'allouer avec new, C++ nous offre l� encore un moyen simple de g�rer cela, std::unique_ptr :

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 
}
local_object sera lib�r� (appel de delete) d�s que l'on sort de la port�e de la variable.
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.

Mis � jour le 15 octobre 2009 white_tentacle

R�ponse courte�:

Il convient de retarder la d�claration (d�finissante) des variables locales jusqu'au dernier moment. Si en plus la variable peut �tre d�clar�e immuable, ce n'est que mieux.

N.B.�: Cela est �galement applicable au C99, sauf si vous visez la portabilit� avec des compilateurs C non enti�rement compatibles comme les VC++.

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; 
}
En initialisant i � 0 par exemple, on peut observer des comportements erron�s, mais reproductibles. En initialisant result � quelque chose, si h() d�route l'affectation de result, nous aurons toujours un r�sultat non al�atoire. Pas forc�ment celui qu'il aurait convenu, mais il aurait �t� non al�atoire.
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; 
}
Et nous voyons tout de suite qu'il y a une erreur -- au pire, le compilateur nous le rappellera. Il devient n�cessaire de r�organiser le code pour que les choses s'encha�nent comme il se doit.

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�:
C++ Coding Standards, item 15, use const proactively, H.Sutter & A.Alexandrescu
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).

Mis � jour le 6 juillet 2014 Luc Hermitte

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 �a


R�ponse � la question

Liens sous la question
pr�c�dent sommaire suivant
 

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 � 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.