Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Paramètres de fonction : Passage par valeur/pointeur/reference ...

15 réponses
Avatar
Bernie
bonjour,
on m'a posé la ? suivante et j'avoue qu'elle me fait avoir des doutes ...

les passage par pointeur et reference sont identique pour les types de
base et les objets.

Par contre, le passage par copie, que ce passe-t-il si un objet ne
dispose pas de constructeur par copie ?

Dans le cas ou on aurait pas besion d'un constructeur par copie :
Si l'objet possede un ptr vers un tableau dynamique, je suppose qu'il
faut aussi que l'objet dispose d'un ptr vers une section critique pour
que les routines de l'objet qui manipule ce tableau se verouillent les
unes les autres (appels venant de l'original et appel venant de la copie) ?

merci d'avance, j'aurais grandement besions de quelques éclaircissement.

10 réponses

1 2
Avatar
Marc Boyer
Le 14-11-2005, Bernie a écrit :
les passage par pointeur et reference sont identique pour les types de
base et les objets.


Modulo la valeur NULL peut-être.

Par contre, le passage par copie, que ce passe-t-il si un objet ne
dispose pas de constructeur par copie ?


Que signifie pour toi "ne dispose pas de".

Si le programmeur ne donne pas de constructeur de copie,
le compilateur en génére un par défaut. Donc, il existe.
On peut par contre avoir un constructeur privé (donc
inutilisable pour les autres).
On peut aussi déclarer un constructeur de copie
et ne pas le définir.

Tout cela donne des choses différentes.

Dans le cas ou on aurait pas besion d'un constructeur par copie :
Si l'objet possede un ptr vers un tableau dynamique, je suppose qu'il
faut aussi que l'objet dispose d'un ptr vers une section critique pour
que les routines de l'objet qui manipule ce tableau se verouillent les
unes les autres (appels venant de l'original et appel venant de la copie) ?


Je n'ai pas compris la question. A ma connaissance, la notion de
section critique n'a lieu d'être que quand on parle de multi-thread,
et le langage C++ ne connait pas grand chose au multi-thread.

Marc Boyer
--
À vélo, prendre une rue à contre-sens est moins dangereux
que prendre un boulevard dans le sens légal. À qui la faute ?

Avatar
Fabien LE LEZ
On Mon, 14 Nov 2005 17:43:07 +0100, Bernie :

Par contre, le passage par copie, que ce passe-t-il si un objet ne
dispose pas de constructeur par copie ?


Un objet a toujours un constructeur de copie (au moins déclaré, si non
implémenté).
Il peut être privé, auquel cas la compilation échouera.
Il peut n'être pas défini, auquel cas le linker va râler si jamais on
arrive jusque-là.

Dans le cas ou on aurait pas besion d'un constructeur par copie :
Si l'objet possede un ptr vers un tableau dynamique,


Qu'appelles-tu "tableau dynamique" ?

je suppose qu'il
faut aussi que l'objet dispose d'un ptr vers une section critique pour
que les routines de l'objet qui manipule ce tableau se verouillent les
unes les autres (appels venant de l'original et appel venant de la copie) ?


J'ai un peu de mal à comprendre ta question.

Si la logique de ton programme fait que deux threads peuvent accéder à
la même variable en même temps, il te faut a priori un mutex. Les
"critical sections" de Windows sont un exemple de mutex.

À toi de voir si cette règle concerne ton cas particulier.

Étant assez peu expérimenté en matière de multithread, j'applique
aussi une autre règle : moins il y a de variables accessibles par
plusieurs threads, mais j'aurai de bugs pénibles à déboguer.

Avatar
Alexandre
bonsoir,

"Bernie" a écrit dans le message de news:
dlaeqe$a7p$
bonjour,
on m'a posé la ? suivante et j'avoue qu'elle me fait avoir des doutes ...

les passage par pointeur et reference sont identique pour les types de
base et les objets.


à quelques nuances près, tout de même. Un passage "par pointeur" est en fait
un passage "par valeur", donc avec une recopie (mais recopie du pointeur,
pas de la valeur pointée bien sur). Dans la pratique, tu peux dire que dans
les deux cas l'objet qui t'interesse n'est pas recopié


Par contre, le passage par copie, que ce passe-t-il si un objet ne dispose
pas de constructeur par copie ?


passage "par valeur" je suppose ?
Un objet dispose toujours d'un constructeur par copie.


Dans le cas ou on aurait pas besion d'un constructeur par copie :
Si l'objet possede un ptr vers un tableau dynamique, je suppose qu'il faut
aussi que l'objet dispose d'un ptr vers une section critique pour que les
routines de l'objet qui manipule ce tableau se verouillent les unes les
autres (appels venant de l'original et appel venant de la copie) ?


tout ceci n'est pas très clair ;-)


merci d'avance, j'aurais grandement besions de quelques éclaircissement.


Et bien disons en gros, que lorsque tu passes par valeur, outre la durée de
passage qui peut être importante (puisqu'il y a copie d'un objet), ça
suppose que l'objet en question est COPIABLE correctement, que deux
instances possédant la même valeur ont le même comportement.
Exemple d'objet non copiable correctement :

class Tableau
{
int *ptr_tab;
public:
Tableau(int size){ptr_tab = new int[size];}
~Tableau(){delete[] ptr_tab;}
int& operator[](int i){return ptr_tab[i];}
};

Si tu passes un "Tableau" par valeur, vu que nul constructeur par copie
n'est défini, tu va créer une 2e instance possédant le même pointeur (la
même valeur pour ptr_tab), donc il n'y aura pas de 2e allocation. Quand
cette 2e instance va "mourir" (à la fin de sa portée), son destructeur va
libérer la mémoire allouée. Or l'objet original est sensé toujours
"posséder" cette instance, son destructeur va la libérer, et là :
comportement indéfini (delete d'un pointeur invalide).
Si je ne suis pas clair :

void Affiche(Tableau t, int taille) // passage par valeur (à éviter...)
{
for(int i=0;i<taille;i++)
std::cout<<t[i]<<' '; // affichage correct
} // fin de portée de t, donc destructeur appelé, donc delete !

{
Tableau t1(10); // ici on alloue un espace mémoire de 10 int dont
l'adresse est stockée dans t1
for(int i=0;i<10;i++) t1[i]=i;
Affiche(t1);
} // ici on arrive en fin de portée de t1, donc ~Tableau() est appelé,
donc BOUM !

Pour éviter ça, que fait-on ?
On évite les passage par valeur pour autre chose que les types intégrés
(int, bool, float, etc...) et tout cas on les reserve aux objets qui SONT
des valeurs (ie sont copiables, donc possèdent si nécessaires constructeur
par copie ET opérateur =)
On fait en sorte que l'objet SOIT une valeur (définition constructeur
copie/operator=)
On empeche la compilation si passage par valeur (il suffit de définir
constructeur par copie privé avec code vide)

En espérant avoir été clair et le + complet possible...
Et ne pas avoir sorti de bétise ;-)

Avatar
Bernie
En espérant avoir été clair et le + complet possible...
Et ne pas avoir sorti de bétise ;-)



Merci, avec toutes les remarques et conseils récupérés ici, ca
rafraichis un peu mes connaissances ....

Avatar
kanze
Marc Boyer wrote:

les passage par pointeur et reference sont identique pour
les types de base et les objets.


Modulo la valeur NULL peut-être.


Ainsi que le fait qu'on peut passer un temporaire à une
référence const.

(Sans parler de la syntaxe, évidemment.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Fabien LE LEZ wrote:

[...]
Étant assez peu expérimenté en matière de multithread,
j'applique aussi une autre règle : moins il y a de variables
accessibles par plusieurs threads, mais j'aurai de bugs
pénibles à déboguer.


Moi, je commence à avoir pas mal d'expérience en matière
multithread, et j'applique la même règle.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Fabien LE LEZ
On Mon, 14 Nov 2005 22:26:53 +0100, "Alexandre"
:

On empeche la compilation si passage par valeur (il suffit de définir
constructeur par copie privé

avec code vide)


Non, avec pas de code du tout. Ce qui permet d'avoir une erreur de
link si jamais le constructeur de copie est quand même appelé (dans
une fonction membre par exemple).

Il me semble que la règle à appliquer est très simple :
Si le constructeur de copie ou l'opérateur de copie généré par le
compilateur ne convient pas, il faut déclarer les deux, et les définir
si ça a un sens.

Je n'ai jamais eu l'occasion de définir le constructeur de copie et
pas l'opérateur de copie ; je ne sais pas si c'est envisageable.

Avatar
Alexandre
bonsoir,

Non, avec pas de code du tout. Ce qui permet d'avoir une erreur de
link si jamais le constructeur de copie est quand même appelé (dans
une fonction membre par exemple).


ça c'est pas bête, tiens. Je garde l'idée, merci ;-)


Il me semble que la règle à appliquer est très simple :
Si le constructeur de copie ou l'opérateur de copie généré par le
compilateur ne convient pas, il faut déclarer les deux, et les définir
si ça a un sens.

Je n'ai jamais eu l'occasion de définir le constructeur de copie et
pas l'opérateur de copie ; je ne sais pas si c'est envisageable.


à vrai dire je considère aussi qu'ils vont toujours ensemble ces deux là ;-)
Cas particulier : pour un singleton, inutile de définir l'opérateur de
copie...

Avatar
Fabien LE LEZ
On Tue, 15 Nov 2005 18:30:24 +0100, "Alexandre"
:

Cas particulier : pour un singleton, inutile de définir l'opérateur de
copie...


Ce n'est pas un "cas particulier" : un singleton est non-copiable
(puisque par définition il n'en existe qu'une instance), donc ni le
constructeur de copie, ni l'opérateur de copie ne doivent être
définis.

Avatar
kanze
Fabien LE LEZ wrote:
On Mon, 14 Nov 2005 22:26:53 +0100, "Alexandre"
:

Je n'ai jamais eu l'occasion de définir le constructeur de
copie et pas l'opérateur de copie ; je ne sais pas si c'est
envisageable.


Si par opérateur de copie, tu veux dire l'affectation, je dirais
que c'est même assez courant dans les logiciels transactionnels.
Typiquement, les objets concernés par une transaction sont des
objets « entité », dont l'identité a de l'importance. (Au moins
à l'intérier d'une transaction. Ils ont en général un
identificateur, du genre nom ou numéro de série, qui sert plutôt
que l'adresse à établir l'identité entre des transactions.) Ils
ne supportent donc ni la copie ni l'affectation par le code
client. En revanche, le gestionnaire des transactions en fait
bien une copie, afin de pouvoir faire un roll-back si la
transaction échoue.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

1 2