Besoin d'éclaircissement : this et const (questions simples)
Bonjour.
Il y a quelque temps, je codais encore "simplement" en C++ et je n'avais pas de problème particulier relatif à ce langage. Mais récemment j'ai eu besoin de surcharger des opérateurs et depuis que j'ai appris à faire cela, un ensemble de choses est devenu beaucoup moins clair :calim2: .
Prenons un exemple simple : une classe Vector2 avec 2 variables membres double m_x et double m_y qui fait des opérations sur des vecteurs du plan. L'ensemble des opérateurs ont été surchargés de façon à faire simplement des opérations sur des vecteurs.
Question 1 : const et &
Prenons un exemple simple : une fonction addelem(unsigned int n,double a) de la classe Vector2 qui ajoute a à m_x si n=1 et a à m_y si n=2. Quelle est la différence entre intituler la fonction addelem(unsigned int n,double a) et addelem(const unsigned int &n, const double &a) ? Quelle est la "meilleure" écriture ? (j'aurai tendance à dire que la seconde à la meilleure car elle évite de recopier n et a en mémoire mais je ne suis pas du tout sûr de moi).
Question 2 : fonctions constantes
J'ai lu qu'il était souvent préférable de placer le mot const après toutes les fonctions get d'une classe (l'argument avancé est la protection des variables membres). Est-ce qu'il est conseillé d'ajouter const après toutes les fonctions d'une classe qui ne touchent pas aux variables membres ? Si oui/non, pourquoi ? La seule fonction du const est-il de protéger les variables membres ?
Question 3 : utilisation de this
En gardant l'exemple de la fonction addelem(unsigned int n,double a), j'ai à première vue 2 choix : soit cette fonction renvoie un nouveau Vector2 résultat de l'opération sans modifier le vecteur original, soit elle est de type void et elle modifie m_x ou m_y. Dans le premier cas, si je déclare un Vector2 v dans le programme principal, je pourrai écrire v=v.addelem(1,3.6) si je veux modifier v alors que dans le second cas, je pourrai écrire seulement v.addelem(1,3.6). L'inconvénient du second cas est que cela m'empêche de faire des opérations en même temps avec le nouveau vecteur puisque la fonction est de type void.
Est-ce que ce troisième code permet "d'allier" les 2 cas précédents :
Code:
1 2 3 4 5 6 7 8 9
| Vector2 Vector2::addelem(unsigned int n,double a)
{
switch(n)
{
case 1 : m_x+=a; break;
case 2 : m_y+=a; break;
}
return *this;
} |
en me permettant à la fois d'écrire v.addelem(1,3.6) si j'ai juste besoin de modifier le vecteur, mais aussi de faire des opérations avec le vecteur modifié ? Quelle est "dans la pratique" ce qui est le plus couramment utilisé dans du vrai beau code C++ ?
Merci beaucoup à ceux qui m'aideront à y voir plus clair :ccool:
De l'importance d'être constant.
Salut,
Citation:
Envoyé par
Kaluza
Question 1 : const et &
Prenons un exemple simple : une fonction addelem(unsigned int n,double a) de la classe Vector2 qui ajoute a à m_x si n=1 et a à m_y si n=2. Quelle est la différence entre intituler la fonction addelem(unsigned int n,double a) et addelem(const unsigned int &n, const double &a) ? Quelle est la "meilleure" écriture ? (j'aurai tendance à dire que la seconde à la meilleure car elle évite de recopier n et a en mémoire mais je ne suis pas du tout sûr de moi).
La réponse n'est pas immédiate et aussi simple que oui ou non. D'abord comprendre les 2 écritures :
Code:
1 2
| void fonction(type parametre_); // (1)
void fonction(type const & parametre_); // (2) |
(1) : lors de l'appel de la fonction, le paramètre est copié et c'est cette copie qui est transmise à la fonction.
(2) : lors de l'appel de la fonction, le paramètre n'est pas copié, c'est une référence qui est transmise à la fonction. Un peu comme une adresse si ça avait été un pointeur. Comme la fonction va manipuler via la référence le même paramètre que la fonction appelant, le const permet d'indiquer que la fonction ne va pas le modifier.
A priori, on pourrait penser que (2) est préférable à (1) puisqu'on ne fait pas une copie du paramètre. C'est vrai dans la plus part des cas ... sauf en général pour les types primitifs (char, short, int, float, double,...), les itérateurs et les foncteurs. En général, les types primitifs 'coûtent' au mieux - chers, au pire pareil. cf cette discussion et ce message.
Citation:
Envoyé par
Kaluza
Question 2 : fonctions constantes
J'ai lu qu'il était souvent préférable de placer le mot const après toutes les fonctions get d'une classe (l'argument avancé est la protection des variables membres). Est-ce qu'il est conseillé d'ajouter const après toutes les fonctions d'une classe qui ne touchent pas aux variables membres ? Si oui/non, pourquoi ? La seule fonction du const est-il de protéger les variables membres ?
const indique que la fonction ne va pas modifier l'état interne de l'objet. Il est important de le préciser si c'est le cas car cela fait parti du 'contrat' de la classe. D'une part, le compilateur va râler si tu voulais ne pas modifier l'objet mais qu'incidemment tu le modifies. Mais d'autre part, tu renforce la sémantique de cette fonction. Et puis c'est un élément de documentation très fort pour l'utilisation de cette fonction. cf F.A.Q Pourquoi certaines fonctions membres possèdent le mot clé const après leur nom ? et Qu'est-ce que l'immuabilité ?
Citation:
Envoyé par
Kaluza
Question 3 : utilisation de this
En gardant l'exemple de la fonction
addelem(unsigned int n,double a), j'ai à première vue 2 choix : soit cette fonction renvoie un nouveau
Vector2 résultat de l'opération sans modifier le vecteur original, soit elle est de type
void et elle modifie
m_x ou
m_y. Dans le premier cas, si je déclare un
Vector2 v dans le programme principal, je pourrai écrire
v=v.addelem(1,3.6) si je veux modifier
v alors que dans le second cas, je pourrai écrire seulement
v.addelem(1,3.6). L'inconvénient du second cas est que cela m'empêche de faire des opérations en même temps avec le nouveau vecteur puisque la fonction est de type void.
Est-ce que ce troisième code permet "d'allier" les 2 cas précédents :
Code:
1 2 3 4 5 6 7 8 9
| Vector2 Vector2::addelem(unsigned int n,double a)
{
switch(n)
{
case 1 : m_x+=a; break;
case 2 : m_y+=a; break;
}
return *this;
} |
en me permettant à la fois d'écrire
v.addelem(1,3.6) si j'ai juste besoin de modifier le vecteur, mais aussi de faire des opérations avec le vecteur modifié ? Quelle est "dans la pratique" ce qui est le plus couramment utilisé dans du vrai beau code C++ ?
A nouveau la réponse n'est pas binaire et dépend de ce que tu veux faire :
Code:
1 2 3
| void addelem(unsigned int n,double a); // (1)
Vector2 addelem(unsigned int n,double a); // (2)
Vector2& addelem(unsigned int n,double a); // (3) |
(1) ne retourne rien et donc tu ne peux récupérer le résultat comme un nouveau vecteur ou enchaîner les fonctions.
(2) retourne une copie du résultat. Tu peux donc récupérer cette nouvelle valeur
(3) souvent utilisé pour les opérations retourne une référence sur le résultat et permet d'enchainer les opérations sur le même objet : v.addelem(1,1.5).addelem(2,2.5);