Je suis nouveau dans la programmation C ++, mais j'ai de l'expérience en Java. J'ai besoin de conseils sur la façon de passer des objets à des fonctions en C ++.
Dois-je transmettre des pointeurs, des références ou des valeurs sans pointeur et sans référence? Je me souviens qu'en Java, il n'y a pas de tels problèmes puisque nous passons juste la variable qui contient la référence aux objets.
Ce serait formidable si vous pouviez également expliquer où utiliser chacune de ces options.
Réponses:
Règles générales pour C ++ 11:
Passez par valeur , sauf lorsque
const
référence ,const
référence non lvalue ,const
référence ou non.)Passer par un pointeur n'est pratiquement jamais conseillé. Les paramètres facultatifs sont mieux exprimés en tant que
std::optional
(boost::optional
pour les anciennes bibliothèques std), et l'aliasing se fait bien par référence.La sémantique de mouvement de C ++ 11 rend le passage et le retour en valeur beaucoup plus attrayants même pour des objets complexes.
Règles générales pour C ++ 03:
Passez les arguments par
const
référence , sauf lorsqueconst
référenceNULL
/0
/ à lanullptr
place; appliquer la règle précédente pour déterminer si vous devez passer par un pointeur sur unconst
argument(ici, "passer par valeur" est appelé "passer par copie", car le passage par valeur crée toujours une copie en C ++ 03)
Il y a plus à cela, mais ces quelques règles pour débutants vous mèneront assez loin.
la source
Il existe certaines différences dans les conventions d'appel en C ++ et Java. En C ++, il n'y a techniquement que deux conventions: pass-by-value et pass-by-reference, avec de la littérature incluant une troisième convention pass-by-pointer (qui est en fait pass-by-value d'un type de pointeur). En plus de cela, vous pouvez ajouter de la constance au type d'argument, améliorant la sémantique.
Passer par référence
Le passage par référence signifie que la fonction recevra conceptuellement votre instance d'objet et non une copie de celle-ci. La référence est conceptuellement un alias de l'objet qui a été utilisé dans le contexte d'appel et ne peut pas être nulle. Toutes les opérations effectuées à l'intérieur de la fonction s'appliquent à l'objet en dehors de la fonction. Cette convention n'est pas disponible en Java ou C.
Passer par valeur (et passer par pointeur)
Le compilateur générera une copie de l'objet dans le contexte d'appel et utilisera cette copie à l'intérieur de la fonction. Toutes les opérations effectuées à l'intérieur de la fonction sont effectuées sur la copie, pas sur l'élément externe. Il s'agit de la convention pour les types primitifs en Java.
Une version spéciale de celui-ci passe un pointeur (adresse de l'objet) dans une fonction. La fonction reçoit le pointeur, et toutes les opérations appliquées au pointeur lui-même sont appliquées à la copie (pointeur), d'autre part, les opérations appliquées au pointeur déréférencé s'appliqueront à l'instance d'objet à cet emplacement de mémoire, donc la fonction peut avoir des effets secondaires. L'effet de l'utilisation de la valeur de passage d'un pointeur sur l'objet permettra à la fonction interne de modifier les valeurs externes, comme avec la référence par passage et permettra également des valeurs facultatives (passez un pointeur nul).
C'est la convention utilisée en C lorsqu'une fonction a besoin de modifier une variable externe, et la convention utilisée en Java avec les types de référence: la référence est copiée, mais l'objet référencé est le même: les modifications apportées à la référence / pointeur ne sont pas visibles à l'extérieur la fonction, mais les modifications apportées à la mémoire pointée sont.
Ajout de const à l'équation
En C ++, vous pouvez attribuer une constante aux objets lors de la définition de variables, de pointeurs et de références à différents niveaux. Vous pouvez déclarer une variable constante, vous pouvez déclarer une référence à une instance constante, et vous pouvez définir tous les pointeurs vers des objets constants, des pointeurs constants vers des objets mutables et des pointeurs constants vers des éléments constants. Inversement, en Java, vous ne pouvez définir qu'un seul niveau de constance (mot-clé final): celui de la variable (instance pour les types primitifs, référence pour les types de référence), mais vous ne pouvez pas définir de référence à un élément immuable (sauf si la classe elle-même est immuable).
Ceci est largement utilisé dans les conventions d'appel C ++. Lorsque les objets sont petits, vous pouvez passer l'objet par valeur. Le compilateur va générer une copie, mais cette copie n'est pas une opération coûteuse. Pour tout autre type, si la fonction ne change pas l'objet, vous pouvez passer une référence à une instance constante (généralement appelée référence constante) du type. Cela ne copiera pas l'objet, mais le passera dans la fonction. Mais en même temps, le compilateur garantira que l'objet n'est pas modifié à l'intérieur de la fonction.
Règles de base
Voici quelques règles de base à suivre:
Il existe d'autres petits écarts par rapport à ces règles, le premier étant la gestion de la propriété d'un objet. Lorsqu'un objet est alloué dynamiquement avec new, il doit être désalloué avec delete (ou ses versions []). L'objet ou la fonction responsable de la destruction de l'objet est considéré comme le propriétaire de la ressource. Lorsqu'un objet alloué dynamiquement est créé dans un morceau de code, mais que la propriété est transférée vers un élément différent, cela se fait généralement avec une sémantique de passage par pointeur, ou si possible avec des pointeurs intelligents.
Note d'accompagnement
Il est important d'insister sur l'importance de la différence entre les références C ++ et Java. En C ++, les références sont conceptuellement l'instance de l'objet, pas un accesseur à celui-ci. L'exemple le plus simple consiste à implémenter une fonction d'échange:
La fonction d'échange ci-dessus modifie ses deux arguments par l'utilisation de références. Le code le plus proche en Java:
La version Java du code modifiera les copies des références en interne, mais ne modifiera pas les objets réels en externe. Les références Java sont des pointeurs C sans arithmétique de pointeur qui sont passés par valeur dans les fonctions.
la source
Il y a plusieurs cas à considérer.
Paramètre modifié (paramètres "out" et "in / out")
Ce cas concerne principalement le style: voulez-vous que le code ressemble à call (obj) ou call (& obj) ? Cependant, il y a deux points où la différence est importante: le cas facultatif ci-dessous et vous souhaitez utiliser une référence lors de la surcharge des opérateurs.
... et en option
Paramètre non modifié
C'est le cas intéressant. La règle générale est que les types "bon marché à copier" sont passés par valeur - ce sont généralement de petits types (mais pas toujours) - tandis que d'autres sont passés par const ref. Cependant, si vous devez faire une copie dans votre fonction, vous devez passer par valeur . (Oui, cela expose un peu de détails d'implémentation. C'est le C ++. )
... et en option
Il y a la moindre différence ici entre toutes les situations, alors choisissez celle qui vous facilite la vie.
Const by value est un détail d'implémentation
Ces déclarations sont en fait exactement la même fonction! Lors du passage par valeur, const est purement un détail d'implémentation. Essaye le:
la source
const
être une implémentation lors du passage par valeur.Passez par valeur:
Passez les variables par valeur lorsque la fonction a besoin d'une isolation complète de l'environnement, c'est-à-dire pour empêcher la fonction de modifier la variable d'origine ainsi que pour empêcher les autres threads de modifier sa valeur pendant l'exécution de la fonction.
L'inconvénient est les cycles CPU et la mémoire supplémentaire dépensée pour copier l'objet.
Passez par référence const:
Ce formulaire émule le comportement passe-par-valeur tout en supprimant la surcharge de copie. La fonction obtient un accès en lecture à l'objet d'origine, mais ne peut pas modifier sa valeur.
L'inconvénient est la sécurité des threads: toute modification apportée à l'objet d'origine par un autre thread apparaîtra à l'intérieur de la fonction pendant son exécution.
Passez par référence non const:
Utilisez-le lorsque la fonction doit réécrire une valeur dans la variable, qui sera finalement utilisée par l'appelant.
Tout comme le cas de référence const, ce n'est pas thread-safe.
Passer par le pointeur const:
Fonctionnellement identique à passer par référence const, à l'exception de la syntaxe différente, plus le fait que la fonction appelante peut passer le pointeur NULL pour indiquer qu'elle n'a pas de données valides à passer.
Pas thread-safe.
Passer par un pointeur non const:
Similaire à la référence non const. L'appelant définit généralement la variable sur NULL lorsque la fonction n'est pas censée réécrire une valeur. Cette convention se retrouve dans de nombreuses API glibc. Exemple:
Tout comme tous passent par référence / pointeur, pas thread-safe.
la source
Puisque personne n'a mentionné que j'ajoute dessus, lorsque vous passez un objet à une fonction en c ++, le constructeur de copie par défaut de l'objet est appelé si vous n'en avez pas un qui crée un clone de l'objet, puis le transmettez à la méthode, donc lorsque vous modifiez les valeurs d'objet qui se refléteront sur la copie de l'objet au lieu de l'objet d'origine, c'est le problème en c ++, donc si vous faites de tous les attributs de classe des pointeurs, les constructeurs de copie copieront les adresses des attributs de pointeur, donc lorsque la méthode appelle sur l'objet qui manipule les valeurs stockées dans les adresses d'attributs de pointeur, les changements se reflètent également dans l'objet d'origine qui est passé en paramètre, donc cela peut se comporter de la même manière que Java mais n'oubliez pas que toute votre classe les attributs doivent être des pointeurs, vous devez également modifier les valeurs des pointeurs,sera beaucoup plus clair avec l'explication du code.
Mais ce n'est pas une bonne idée car vous finirez par écrire beaucoup de code impliquant des pointeurs, qui sont sujets à des fuites de mémoire et n'oubliez pas d'appeler des destructeurs. Et pour éviter que c ++ ait des constructeurs de copie où vous créerez une nouvelle mémoire lorsque les objets contenant des pointeurs seront passés à des arguments de fonction qui arrêteront de manipuler les données d'autres objets, Java passe par valeur et la valeur est référence, donc il ne nécessite pas de constructeurs de copie.
la source
Il existe trois méthodes pour passer un objet à une fonction en tant que paramètre:
Passez par l'exemple suivant:
Production:
la source
Voici les façons de passer des arguments / paramètres pour fonctionner en C ++.
1. par valeur.
2. par référence.
3. par objet.
la source