Je suis un gars C et j'essaie de comprendre du code C++. J'ai la déclaration de fonction suivante:
int foo(const string &myname) {
cout << "called foo for: " << myname << endl;
return 0;
}
En quoi la signature de fonction diffère-t-elle du C équivalent:
int foo(const char *myname)
Y a-t-il une différence entre l'utilisation de string *myname
contre string &myname
? Quelle est la différence entre &
en C++ et *
en C pour indiquer des pointeurs?
De même:
const string &GetMethodName() { ... }
Quel est le &
faire ici? Y a-t-il un site Web qui explique comment &
est utilisé différemment en C vs C++?
Le "&" indique une référence au lieu d'un pointeur vers un objet (dans votre cas, une référence constante).
L'avantage d'avoir une fonction telle que
foo(string const& myname)
plus de
foo(string const* myname)
est que dans le premier cas, vous êtes assuré que myname est non nul, car C++ n'autorise pas les références NULL. Puisque vous passez par référence, l'objet n'est pas copié, comme si vous passiez un pointeur.
Votre deuxième exemple:
const string &GetMethodName() { ... }
Vous permettrait de renvoyer une référence constante à, par exemple, une variable membre. Ceci est utile si vous ne souhaitez pas qu'une copie soit retournée et que vous soyez à nouveau assuré que la valeur retournée n'est pas nulle. Par exemple, ce qui suit vous permet un accès direct en lecture seule:
class A
{
public:
int bar() const {return someValue;}
//Big, expensive to copy class
}
class B
{
public:
A const& getA() { return mA;}
private:
A mA;
}
void someFunction()
{
B b = B();
//Access A, ability to call const functions on A
//No need to check for null, since reference is guaranteed to be valid.
int value = b.getA().bar();
}
Vous devez bien sûr faire attention à ne pas renvoyer de références invalides. Les compilateurs seront heureux de compiler les éléments suivants (en fonction de votre niveau d'avertissement et de la façon dont vous traitez les avertissements)
int const& foo()
{
int a;
//This is very bad, returning reference to something on the stack. This will
//crash at runtime.
return a;
}
Fondamentalement, il est de votre responsabilité de vous assurer que tout ce à quoi vous renvoyez une référence est réellement valide.
Ici, &
n'est pas utilisé comme opérateur. Dans le cadre des déclarations de fonctions ou de variables, &
désigne une référence. Le C++ FAQ Lite a un joli astucieux chapitre sur les références .
string * et string & diffèrent de deux manières. Tout d'abord, le pointeur pointe vers l'emplacement de l'adresse des données. La référence pointe vers les données. Si vous aviez la fonction suivante:
int foo(string *param1);
Vous devez vérifier la déclaration de fonction pour vous assurer que param1 pointe vers un emplacement valide. Relativement:
int foo(string ¶m1);
Ici, il est de la responsabilité de l'appelant de s'assurer que les données pointées sont valides. Vous ne pouvez pas passer une valeur "NULL", par exemple, dans la deuxième fonction ci-dessus.
En ce qui concerne votre deuxième question, sur les valeurs de retour de méthode comme référence, considérez les trois fonctions suivantes:
string &foo();
string *foo();
string foo();
Dans le premier cas, vous renverriez une référence aux données. Si votre déclaration de fonction ressemblait à ceci:
string &foo()
{
string localString = "Hello!";
return localString;
}
Vous obtiendriez probablement des erreurs de compilation, car vous renvoyez une référence à une chaîne qui a été initialisée dans la pile pour cette fonction. Au retour de la fonction, cet emplacement de données n'est plus valide. En règle générale, vous souhaitez renvoyer une référence à un membre de la classe ou quelque chose comme ça.
La deuxième fonction ci-dessus renvoie un pointeur dans la mémoire réelle, il restera donc le même. Vous devrez cependant vérifier les pointeurs NULL.
Enfin, dans le troisième cas, les données renvoyées seraient copiées dans la valeur de retour de l'appelant. Donc, si votre fonction était comme ça:
string foo()
{
string localString = "Hello!";
return localString;
}
Vous seriez d'accord, car la chaîne "Bonjour" serait copiée dans la valeur de retour de cette fonction, accessible dans l'espace mémoire de l'appelant.
Une façon de regarder l'opérateur & (référence) en c ++ est simplement un sucre syntaxique vers un pointeur. Par exemple, ce qui suit est à peu près équivalent:
void foo(int &x)
{
x = x + 1;
}
void foo(int *x)
{
*x = *x + 1;
}
Le plus utile est lorsque vous traitez avec une classe, afin que vos méthodes passent de x-> bar () à x.bar ().
La raison pour laquelle j'ai dit à peu près est que l'utilisation de références impose des restrictions de compilation supplémentaires sur ce que vous pouvez faire avec la référence, afin de vous protéger contre certains des problèmes causés par les pointeurs. Par exemple, vous ne pouvez pas modifier accidentellement le pointeur ou utiliser le pointeur autrement que pour référencer l'objet singulier que vous avez passé.
#include<iostream>
using namespace std;
int add(int &number);
int main ()
{
int number;
int result;
number=5;
cout << "The value of the variable number before calling the function : " << number << endl;
result=add(&number);
cout << "The value of the variable number after the function is returned : " << number << endl;
cout << "The value of result : " << result << endl;
return(0);
}
int add(int &p)
{
*p=*p+100;
return(*p);
}
Il s'agit d'un code invalide à plusieurs titres. L'exécuter via g ++ donne:
crap.cpp: In function ‘int main()’:
crap.cpp:11: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int*’
crap.cpp:3: error: in passing argument 1 of ‘int add(int&)’
crap.cpp: In function ‘int add(int&)’:
crap.cpp:19: error: invalid type argument of ‘unary *’
crap.cpp:19: error: invalid type argument of ‘unary *’
crap.cpp:20: error: invalid type argument of ‘unary *’
Une version valide du code se lit comme suit:
#include<iostream>
using namespace std;
int add(int &number);
int main ()
{
int number;
int result;
number=5;
cout << "The value of the variable number before calling the function : " << number << endl;
result=add(number);
cout << "The value of the variable number after the function is returned : " << number << endl;
cout << "The value of result : " << result << endl;
return(0);
}
int add(int &p)
{
p=p+100;
return p;
}
Ce qui se passe ici, c'est que vous passez une variable "en l'état" à votre fonction. Cela équivaut à peu près à:
int add(int *p)
{
*p=*p+100;
return *p;
}
Cependant, passer une référence à une fonction garantit que vous ne pouvez pas faire des choses comme l'arithmétique du pointeur avec la référence. Par exemple:
int add(int &p)
{
*p=*p+100;
return p;
}
est invalide.
Si vous devez utiliser un pointeur vers une référence, cela doit être fait explicitement:
int add(int &p)
{
int* i = &p;
i=i+100L;
return *i;
}
Qui, lors d'un test, donne (comme prévu) une sortie indésirable:
The value of the variable number before calling the function : 5
The value of the variable number after the function is returned : 5
The value of result : 1399090792
Votre fonction déclare une référence constante à une chaîne:
int foo(const string &myname) {
cout << "called foo for: " << myname << endl;
return 0;
}
Une référence possède des propriétés spéciales, qui en font une alternative plus sûre aux pointeurs à bien des égards:
En quoi la signature de fonction diffère-t-elle du C équivalent:
int foo(const char *myname)
Il existe plusieurs différences, car la première fait directement référence à un objet, tandis que const char*
doit être déréférencé pour pointer vers les données.
Y a-t-il une différence entre l'utilisation de string * myname vs string & myname?
La principale différence dans le traitement des paramètres est que vous n'avez pas besoin de déréférencer &myname
. Un exemple plus simple est:
int add_ptr(int *x, int* y)
{
return *x + *y;
}
int add_ref(int &x, int &y)
{
return x + y;
}
qui font exactement la même chose. La seule différence dans ce cas est que vous n'avez pas besoin de déréférencer x
et y
car ils se réfèrent directement aux variables transmises.
const string &GetMethodName() { ... }
Que fait le & ici? Existe-t-il un site Web qui explique comment et est utilisé différemment en C vs C++?
Cela renvoie une référence constante à une chaîne. Ainsi, l'appelant peut accéder directement à la variable renvoyée, mais uniquement en lecture seule. Ceci est parfois utilisé pour renvoyer des membres de données de chaîne sans allouer de mémoire supplémentaire.
Il y a quelques subtilités avec des références - jetez un œil au C++ FAQ on References pour plus de détails.
Dans ce contexte &
oblige la fonction à prendre stringname
par référence. La différence entre les références et les pointeurs est:
NULL
n'est pas une valeur valide pour une référence et entraînera une erreur de compilation. Donc, généralement, si vous souhaitez utiliser un paramètre de sortie (ou un pointeur/référence en général) dans une fonction C++ et que la transmission d'une valeur nulle à ce paramètre doit être autorisée, utilisez un pointeur (ou un pointeur intelligent, de préférence). Si le passage d'une valeur nulle n'a aucun sens pour cette fonction, utilisez une référence.a
en écrivant b = 4;
. La valeur d'une référence est la valeur de tout ce qu'elle a référencé.