fonction:passage par valeur ou référence
bonjour à tous,
La fonction FcIntStr,dont code ci-dessous, ne répond pas à mon attente.
Je voudrais une sortie sous les lignes Ch ou CA de préférence à celle de St.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
//essai
//#include <string>
#include <iostream>
#include <sstream>
//#include <fstream>
using namespace std;
string FcIntStr(int NbA,string ChA,int fcvue)
{//29/12/11/22h
//IntVerStr=FcIntStr(nb,ch,fcvue);
ostringstream ossNbA;
ossNbA<<NbA;
ChA=ossNbA.str();
return ChA;
}//29/12/11/22h
int main()
{
int fcvue=0;
cout<<"essai int vers string"<<endl;
int nb=15;
string Ch;
string ChA;
string IntVerStr;
IntVerStr=FcIntStr(nb,Ch,fcvue);
cout<<"St "<<IntVerStr<<endl;
cout<<"It "<<nb <<endl;
cout<<"CA "<<ChA<<"*"<<endl;//pourquoi vide?
cout<<"Ch "<<Ch <<"*"<<endl;//pourquoi vide?
cout<<"<E>";cin.get();
} |
La fonction doit être mal écrite(8O)?
Merci pour cette étude.
Petit cours express sur les arguments de fonctions
Bon, reprenons...
Il existe, en C++, trois possibilités pour passer une variable en argument à une fonction:
- Par valeur : en utilisant uniquement le type de la variable qui sera passée et en fournissant le nom pour l'argument (ex: void foo(Type value) )
- Par référence : en insérant le symbole & (qui est le symbole qui indique que l'on veut utiliser une référence sur un objet) après le nom de type, et avant le nom de l'argument (ex void bar( Type & reference ) )
- Par pointeur : en insérant le symbole * (qui est le symbole qui indique que l'on utilise un pointeur) après le nom du type et avant le nom de l'argument (ex : void truc(Type * pointer ) )
Lorsque l'on transmet la variable par valeur, il y a copie de celle-ci au sein de la fonction appelée.
Il n'y a donc aucune relation entre les modifications apportée au sein de la fonction appelée et la variable qui a été transmise dans la fonction appelante, et ce, meme si le nom de l'argument et le nom de la variable qui a été transmise sont les mêmes
Par exemple, le code
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
#include <iostream>
void foo(int value)
{
value *=10;
std::cout<<"dans foo() : value = "<<value<<std::endl;
}
int main()
{
int value = 15;
std::cout<<"avant foo() : value ="<<value<<std::endl;
foo( value );
std::cout<<"apres foo() : value = "<<value<<std::endl;
return 0;
} |
donnera la sortie
Code:
1 2 3
| avant foo() : value = 15
dans foo() : value = 150
apres foo() : value = 15 |
Lorsque l'on transmet la variable par référence, on transmet en réalité un alias de la variable: Il n'y a pas de copie de la variable transmise et, même si c'est l'argument porte un autre nom que la variable d'origine, tout ce qui arrive à l'argument dans la fonction appelée est répercuté sur la variable dans la fonction appelante:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void bar( int & reference) // le & indique que l'on utilise une référence
{
reference*=10;
std::cout<<"dans bar() : reference= "<<reference<<std::endl;
}
int main()
{
int value = 15;
std::cout<<"avant bar() : value ="<<value<<std::endl;
bar( value );
std::cout<<"apres bar() : value = "<<value<<std::endl;
return 0;
} |
donnera la sortie
Code:
1 2 3
| avant bar() : value = 15
dans bar() : reference = 150
apres bar() : value = 150 |
avec le passage par pointeur, on transmet l'adresse mémoire à laquelle se trouve un objet du type est indiqué.
Comme c'est une adresse, il y a copie de l'adresse mémoire à laquelle l'objet se trouve, mais il n'y a pas copie de l'objet en lui-même.
La grosse différence qu'il y a entre le passage d'argument par référence et le passage d'argument par pointeur tient dans le fait qu'une référence apporte une garantie de non nullité (en dehors du fait qu'elle utilise la même syntaxe que l'objet lui-même pour accéder à son contenu ;) ) : Lorsque l'on transmet une référence sur un objet, l'objet en question doit exister, alors que l'on peut transmettre une adresse mémoire connue comme étant invalide (NULL ou, en C++11, nullptr) lorsque la fonction attend un pointeur.
Cependant, si on accède à "ce qui est pointé" par le pointeur (à condition qu'il pointe vers une adresse valide, évidemment :D ) tout ce qui arrive à "ce qui est pointé" par le pointeur dans la fonction appelée arrive également ( fatalement :D ) à la variable dans la fonction appelante.
Je passe volontairement une grande partie d'explications sur les pointeurs, tu n'auras qu'à relire un tuto ou un livre pour savoir de quoi il s'agit (mais saches néanmoins que les pointeurs présentent une série d'inconvénients qui font que l'on préfère généralement le passage par référence au passage par pointeur à moins que l'on n'ait vraiment pas le choix ;))
Ainsi, le code
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void bar( int * pointer) // le * indique que l'on utilise un pointeur
{
*pointer += 10; // le * indique que l'on accède à "ce qui est pointé" par pointer
std::cout<<"dans bar() : *pointer = "<<*pointer<<std::endl; //idem
}
int main()
{
int value = 15;
std::cout<<"avant bar() : value ="<<value<<std::endl;
bar( &value ); // le & indique que l'on prend l'adresse mémoire à laquelle se trouve value
std::cout<<"apres bar() : value = "<<value<<std::endl;
return 0;
} |
donnera la sortie
Code:
1 2 3
| avant bar() : value = 15
dans bar() : *pointer = 25
apres bar() : value = 25 |
Pour être complet, il existe une quatrième possibilité, qui n'est en définitive qu'une particularité du passage par référence: le passage par référence constante.
Il est en effet possible d'indiquer au compilateur qu'il doit considérer une variable (ou une référence sur une variable) comme étant constante en rajoutant le mot clé const après le nom du type: Type const myConstant; ou void bidule(Type const & MyConstantReference).
nota: d'après la norme, le mot clé const s'applique à ce qui se trouve à sa gauche sauf s'il n'y a rien à sa gauche, auquel cas il s'applique alors à ce qui se trouve à sa droite :
Type const myConstant; est donc strictement pareil à const Type myConstant; et void bidule(Type const & MyConstantReference) est strictement pareil à void bidule(const Type & MyConstantReference), bien que les premières versions respectent la règle générale et que les deuxièmes profitent de l'exception qui confirme la règle :D
L'avantage de travailler avec des objets constants (ou des références sur des objets constant) tient dans le fait que le compilateur refusera toute instruction qui risque de modifier l'objet (ou l'objet référencé) en question:
Si tu travailles avec des structures ou des classes (car c'est strictement la meme chose en C++, mis à part la visibilité par défaut des membre :D), seules les fonctions membre déclarées constantes (comprend: s'étant engagées à ne pas modifier l'objet) pourront être appelées depuis l'objet constant (ou la référence constante vers un objet)
Par contre, il est tout à fait possible d'invoquer une fonction s'étant engagée à ne pas modifier l'objet depuis un objet non constant (ou une référence constante vers un objet).
Ainsi, si tu as une classe proche de
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class MyClass
{
/* le constructeur de MyClass */
MyClass(int value) : value(value){}
/* une fonction qui s'engage à ne pas modifier l'objet */
void print() const
{
std::cout<< "value :"<<std::endl;
}
/* et une fonction qui ne s'y engage pas */
void multiply(int factor)
{
value *= factor;
}
private:
/* le membre qui nous intéresse */
int value;
}; |
tu pourras invoquer MyClass::multiply et la fonction MyClass::print depuis une fonction qui aura pris un objet de type MyClass par valeur ou depuis une fonction qui aura pris un objet de type MyClass par référence (non constante) mais tu ne pourra invoquer que la fonction MyClass::print dans une fonction qui aura pris un objet de type MyClass sous la forme d'une référence constante
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
void foo(MyClass value)
{
value.multiply(10); // ca marche :D
std::cout<< "dans foo() value::";
value.print(); // ca marche aussi :D
}
void bar(MyClass & reference)
{
value.multiply(10); // ca marche :D
std::cout<< "dans bar() reference::";
value.print(); // ca marche aussi :D
}
void truc(MyClass const & constreference)
{
/* cet appel sera refusé, si tu le décommente, tu auras une erreur
* de compilation
*/
// value.multiply(10);
std::cout<< "dans truc() constreference::";
// ca, par contre, ca marche :D
value.print();
}
int main()
{
MyClass obj1(10);
std::cout<<"avant foo() : obj1::";
obj1.print();
foo(obj1);
std::cout<<"apres foo() : obj1::";
obj1.print();
std::cout<<std::endl;
MyClass obj2(15);
std::cout<<"avant bar() : obj2::";
obj2.print();
foo(obj2);
std::cout<<"apres bar() : obj2::";
obj2.print();
std::cout<<std::endl;
MyClass obj3(25);
std::cout<<"avant truc() : obj3::";
obj3.print();
truc(obj3);
std::cout<<"apres truc() : obj3::";
obj3.print();
return 0;
} |
aura pour sortie
Code:
1 2 3 4 5 6 7 8 9 10 11
| avant foo() : obj1::value = 10
dans foo() : value::value = 100
apres foo() : obj1::value = 10
avant bar() : obj2::value = 15
dans bar() : reference::value = 150
apres bar() : obj2::value = 150
avant truc() : obj3::value = 25
dans truc() : constreference::value = 25
apres truc() : obj3::value = 25 |
Le passage par référence constante permet, en outre, d'utiliser ce que l'on appelle une variable temporaire anonyme dans la fonction appelée.
Ainsi, nous pourrions très bien avoir un code proche de
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
void truc(MyClass const & constreference)
{
/* cet appel sera refusé, si tu le décommente, tu auras une erreur
* de compilation
*/
// value.multiply(10);
std::cout<< "dans truc() constreference::";
// ca, par contre, ca marche :D
value.print();
}
int main()
{
truc(25);
/* la variable qui été créée pour permettre l'appel de truc n'existe
* plus ici :D
*/
return 0;
} |
Comme il existe une possibilité de convertir un int en un objet de type MyClass (grace au constructeur de MyClass), nous pouvons créer une variable anonyme (c'est un objet de type MyClass pour lequel non n'avons pas de nom dans main() ) temporaire (qui n'existe que pour la durée de l'exécution de truc() ) qui sera utilisée dans truc.
Cela ne peut se faire qu'avec le passage par référence constante , car la norme estime qu'il n'y a aucune raison de permettre de modifier la variable référencée par l'argument, étant donné que nous n'aurons aucun moyen d'y accéder dans la fonction appelante (AKA main() )une fois sorti de l'exécution de la fonction appelée (AKA truc() ) ;)
Cela permet, entre autre, de respecter le principe qui conseille de créer les variables le plus près possible de l'endroit où nous en avons besoin tout en évitant d'avoir, par la suite, une variable qui ne servirait plus à rien parce qu'elle n'était utile que pour permettre l'appel de la fonction ;)
En espérant que cette prose t'aie permis d'enfin comprendre ce que l'on entendait par "passage par valeur", "passage par référence (constante)" ou "passage par pointeur", ainsi que l'utilisation qui peut être faite de ces possibilités ;)