OVH Cloud OVH Cloud

[newbie] problème débile

64 réponses
Avatar
Azuriel
Est-ce possible de déclarer une propriété de même classe que celle qui
la possède ?

class A
{
A a;
};

ne compile pas.

10 réponses

3 4 5 6 7
Avatar
Sylvain
kanze wrote on 18/10/2006 09:28:

si tu savais comme je m'en cogne de ces indéfinitions là !!


Tiens, tiens, tiens. On réjoigne une accusation que j'ai fait
dans la passée. Tu t'en fous que ton code soit correct, ou qu'il
est sûr de marcher. Pourvu qu'il ne crashe pas pendant la démo,
c'est ça.


j'indique: le comportement d'un compilo que je n'utilise pas ne
conditionne pas ma façon de coder.

tu reformules en: je me satisferais de code ne tournant que 5mn de démo.

quelle imagination tu as !!!!!!

Donc, tu n'utiliseras jamais Sun CC, ni g++. Ni, probablement
VC++, parce que jusqu'à preuve du contraire, il l'estime
indéfini aussi.


je crois n'avoir utilisé Sun CC qu'une seule fois pour compiler gcc.
et je n'utilise plus gcc depuis des années (10+) - faudra que je ne
remette au Mac mais j'ai pas vraiment le temps.

je ne sais pas ce que tu attends et entends par preuve, mais VC gère
très bien la copie de pointeur -- ah et dans ce monde là on ne recompile
pas tous les matins son code chez son client avec son compilo sur son
architecture; donc en effet on s'autorise quelque raccourcis ayant fait
leurs preuves.

Sylvain.


Avatar
Falk Tannhäuser
Sylvain schrieb:
Falk Tannhäuser wrote on 18/10/2006 02:07:
Sylvain schrieb:
qui s'autorisera à avoir des références nulles devra les tester


On les teste comment, sachant que le compilateur a le droit d'éliminer
le test ?


ton compilateur modifie tes séquencements en virant des tests ??


Pourquoi pas, si le compilateur peut prouver que le test ne sert à rien,
en partant du principe que l'utilisateur est assez mûr pour ne pas
employer des constructions dont la Norme ne définit pas le comportement
(et en plus le dit clairement dans § 8.3.2/4). Une Norme c'est pas pour
les chiens ; son objectif principal est d'établir un contrat entre les
fournisseurs de compilateurs et leurs utilisateurs.

De plus, rien ne garantit que la référence reste nulle lorsque
l'héritage entre en jeu !


?!? héritage ou pas la référence reste ce qu'elle est tant qu'une
nouvelle valeur ne lui est pas assignée.


En fait, je parlais du cas où l'objet désigné par une référence sur la
classe dérivée est liée à une référence sur la classe de base, comme
dans mon exemple lors de l'appel à toto() dans titi().

Si on utilise des pointeurs,
Derived* pD;
Base* pB;
la conversion effectuée lors de l'affectation
pB = pD;
peut très bien nécessiter un ajustement d'adresse car le sous-objet de
type Base n'est pas forcement placé au début de l'objet complet de type
Derived. (C'est typiquement le cas lors de l'héritage multiple ou
virtuel, ou encore - comme dans mon exemple - lorsqu'une classe
polymorphique dérive d'une classe non polymorphique et le compilateur a
l'habitude de placer le pointeur sur la vtable au début de l'objet.) En
revanche, un pointeur NULL est toujours converti en pointeur NULL, sans
ajustement. Le code généré pour l'affectation ci-dessus est alors
équivalent à
if(pD == 0)
pB = 0;
else
pB = reinterpret_cast<Base*>(reinterpret_cast<char*>(pD) -
offset_de_Base_dans_Derived);

En revanche, si on utilise des références, le compilateur peut faire
l'économie du test ; le code
Derived& rD = ...;
Base& rB = rD;
se simplifie alors à

rB = *reinterpret_cast<Base*>(reinterpret_cast<char*>(&rD) -
offset_de_Base_dans_Derived);

ce qui explique le plantage observé dans mon exemple avec certains
compilateurs.

Question à 0,02 € : Qu'affiche-t-il le programme suivant:


avec les compilos que j'utilise ?
la console affiche "Null reference"


Quelque soit le niveau d'optimisation ? Et si main(), toto() et titi()
se trouvent dans des unités de translation différentes ? Et si, au lieu
de l'héritage d'une classe polymorphique depuis une classe non
polymorphique, on met l'héritage multiple (voir ci-dessous) ?

struct Base {
int i;
};
struct Derived : public Base {
virtual ~Derived() {}
};


btw, cette écriture est incorrecte


Elle est correcte sauf si derrière on fait :

et peux justement provoquer des plantes simplement en faisant:

Base* b = new Derived();
delete b;

la virtualité du destructeur doit être définie sur la classe de base,
non sur une classe enfant.


La virtualité du destructeur dans la classe de base sert exactement
lorsqu'on fait un delete d'un objet de classe dérivée désigné par un
pointeur sur la classe de base, et *seulement* dans ce cas-là. Or dans
mon exemple il n'y a pas de delete. (De toute façon, quand on en arrive
à créer de références nulles, pourquoi encore se préoccuper des petits
détails comme ça ? :-P)

J'aurais aussi bien pu écrire
struct Base0 { int i; };
struct Base { int j; };
struct Derived : public Base0, public Base {};

Falk



Avatar
Falk Tannhäuser
Sylvain schrieb:
Il y a l'approche théorique cadré et décrit
par la norme et il y a l'expérience (la vraie vie).


On connaît des cas où des gens ont parcourus des dizaines de kilomètres
sur l'autoroute au contresens sans accident - pourquoi alors se prendre
la tête avec l'approche théorique cadrée et décrite par le code de la
route, puisque l'expérience / la vraie vie montrent que cela ne sert à
rien ?

Falk

Avatar
Loïc Joly

je ne sais pas ce que tu attends et entends par preuve, mais VC gère
très bien la copie de pointeur -- ah et dans ce monde là on ne recompile
pas tous les matins son code chez son client avec son compilo sur son
architecture; donc en effet on s'autorise quelque raccourcis ayant fait
leurs preuves.


Pourrais tu m'indiquer l'endroit dans la doc de VC où ils indiquent
qu'ils ont pour leur compilateur défini ce comportement indéfini dans la
nome ? C'est le genre d'information que je n'ai jamais réussi à trouver.

--
Loïc

Avatar
Sylvain
Loïc Joly wrote on 19/10/2006 01:46:

Pourrais tu m'indiquer l'endroit dans la doc de VC où ils indiquent
qu'ils ont pour leur compilateur défini ce comportement indéfini dans la
nome ? C'est le genre d'information que je n'ai jamais réussi à trouver.


non je ne peux pas. les seuls docs "compilo" que je lise sont les
reference guides; pour le reste je désassemble (trace la majorité du
code sous debuggeur, y compris le CRT).

Sylvain.

Avatar
Sylvain
Falk Tannhäuser wrote on 19/10/2006 01:32:

On connaît des cas où des gens ont parcourus des dizaines de kilomètres
sur l'autoroute au contresens sans accident - pourquoi alors se prendre
la tête avec l'approche théorique cadrée et décrite par le code de la
route, puisque l'expérience / la vraie vie montrent que cela ne sert à
rien ?


ce n'est pas tout à fait mon point (croisant tous les soirs des crétins
roulant tous feux éteints - il fait nuit à 21.00 en ce moment).

savoir s'il est très osé ou non de faire un A(): a(*this) à la place
d'un A(A& r): a(r) remplis un thread mais ne parle pas vraiment de la
vraie vie dont j'ai parlé (ce type de code à relativement peu de chance
d'exister comme tel, ie forgé ainsi sous la seule responsabilité d'un
seul auteur).

à intégrer de nombreuses librairies tierces (pour piloter du hard mais
pas seulement), je suis assez souvent confronté à des ""créations"" où
si les auteurs ont bien appliquées la norme en interne, ils ont
cruellement oublié de réfléchir à l'API utilisateur.

des cas où utiliser un service de base n'est possible que via un
'propriétaire' (représentation d'un user ou d'un device) lui-même lié à
une 'fabrique' (représentation d'une base ou d'un système) est assez
fréquent; pour illustrer:

class Database {
}
struct User {
User(Database&) {}
};
struct Service {
Service(User&) {}
};

or dans ces cas là, il n'est pas rare que des méthodes du Service (voire
tous les besoins d'une application donnée) n'aient nul besoin d'un user
ou de sa base, l'expérience peut même montrer que ces données membre ne
sont jamais utilisées (pour les services utilisés).

dans de tels cas, soit on se paluche toutes ces instances non neutres en
coût, contrainte, etc; soit, et cela peut m'arriver, on bypasse ce qui
ne sert à rien via des refs nulles.

on pourra troller à l'infini sur le c'est-pas-bien et sur le
la-norme-a-dit-pas-défini; je n'ai en aucun cas dit que cela était ou
bien, ou jouissif de faire exprès de pas suivre les voies normalisées.

je ne m'autorise pas à rouler à contre-sens, mais je ne m'autorise pas
non plus (dans un code) à des détours couteux et inutiles.

Sylvain.

Avatar
Serge Paccalin

en partant du principe que l'utilisateur est assez mûr pour ne pas


Je crois qu'avec le recul que l'on a sur l'informatique, on ne peut plus
partir de ce principe, pour quelque logiciel que ce soit.

--
___________
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Pour bien répondre avec Google, ne pas cliquer
-'(__) « Répondre », mais « Afficher les options »,
_/___(_) puis cliquer « Répondre » (parmi les options).

Avatar
kanze
Sylvain wrote:
Falk Tannhäuser wrote on 18/10/2006 02:07:
Sylvain schrieb:
qui s'autorisera à avoir des références nulles devra les tester


On les teste comment, sachant que le compilateur a le droit
d'éliminer le test ?


ton compilateur modifie tes séquencements en virant des tests ??


J'espère bien. Ça fait quand même partie du b a ba de
l'optmisation.

change de compilo !!


Je n'en connais pas que ne les supprime jamais. (Même
l'optimisateur de VC++ n'est pas aussi mauvais.)

De plus, rien ne garantit que la référence reste nulle lorsque
l'héritage entre en jeu !


?!? héritage ou pas la référence reste ce qu'elle est tant qu'une
nouvelle valeur ne lui est pas assignée.


Je crois que tu n'as pas compris ce qu'il a dit.

Question à 0,02 ? : Qu'affiche-t-il le programme suivant:


avec les compilos que j'utilise ?
la console affiche "Null reference"


Mauvais compilateur, changer de compilateur. (J'ai « core
dump » ou de g++ ou de Sun CC.)

struct Base {
int i;
};
struct Derived : public Base {
virtual ~Derived() {}
};


btw, cette écriture est incorrecte et peux justement provoquer
des plantes simplement en faisant:

Base* b = new Derived();
delete b;

la virtualité du destructeur doit être définie sur la classe
de base, non sur une classe enfant.


Tout à fait.

Je crois en effet qu'il a mal formulé son exemple. Puis, quand
il a donné le core dump attendu, il l'a supposé correct. (Ses
commentaires portaient sur la modification d'une valeur nulle ;
son exemple concernait plutôt la suppression d'un test que le
compilateur voit inutile, parce qu'il ne peut jamais donner
qu'un seul résultat.) Le phénomène qu'il décrivait, c'était
plutôt quelque chose du genre :

struct L { int i ; } ;
struct R { int j ; } ;
struct D : L, R {} ;

D* p = 0 ;
D& r = *p ;
R& rr = r ;

L'adresse de la partie R dans D n'est pas l'adresse de D. Quand
je fais l'équivalent avec des pointeurs, le compilateur est
obligé de tester si le pointeur est nul, pour ne pas effectuer la
correction (ajouter 4 ou 8, probablement) si le pointeur est
nul. Dans le cas des références, en révanche, il sait que la
référence n'est pas nul, et n'a donc pas besoin de faire le
test. Ce qui fait que &rr != NULL.

(En fait, je vois que VC++ effectue le test, même dans ce
cas-ci. Ce qui fait penser qu'il supporte bien des références
nulles. Comme extension : ce n'est certainement pas général, et
la vaste majorité des compilateurs ne le font pas.)

--
James Kanze (GABI Software) email:
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
Falk Tannhäuser wrote:

En revanche, si on utilise des références, le compilateur peut faire
l'économie du test ; le code
Derived& rD = ...;
Base& rB = rD;
se simplifie alors à

rB = *reinterpret_cast<Base*>(reinterpret_cast<char*>(&rD) -
offset_de_Base_dans_Derived);

ce qui explique le plantage observé dans mon exemple avec certains
compilateurs.


En fait, je viens d'effectuer un certain nombre de tests, et
j'ai l'impression que VC++ supporte bien le concepte d'une
référence nulle. C'est une extension, par rapport à la norme, et
ça doit rallentir le code pas mal dans certains cas, mais il
génère systèmatiquement le même code pour les références que
pour les pointeurs, au moins dans le peu de cas que j'ai essayé.

Toujours est-il qu'avant d'utiliser cette extension, je
m'assurerai 1) que c'est bien garanti par Microsoft, c-à-d que
c'est documenté comme une extension, et 2) que je n'aurais
jamais besoin de compiler le code avec un autre compilateur
(chose assez difficile à s'assurer dans la mesure que Microsoft
ne livre le compilateur que pour un nombre de cibles extrèmement
limitées). Et évidemment, qu'elle m'apporte quelque chose, chose
que je ne crois pas.

--
James Kanze (GABI Software) email:
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
Gabriel Dos Reis wrote:
"kanze" writes:

[...]

| > pour qui écrit un sample jamais publié (surtout pas utilisé)
| > c'est surement d'une haute importance; quand on est derrière
| > un vrai compilo pour du vrai code, le comportement est tout à
| > fait complètement défini.

| Avec le compilateur CenterLine, oui. Il est garanti de provoquer
| un message d'erreur. Avec les autres compilateurs, je ne sais
| pas. Je ne vois rien de garantie dans la documentation de Sun
| CC, ni dans celle de g++. (Je n'ai rien vu dans la documentation
| de VC++ non plus, mais je ne l'ai pas tout lue.)

Depuis des années, GCC exploite activement ces genres de
fonctionnement indéfini : si tu déréférences un pointeur, GCC en
déduit qu'il n'est pas nul (tu n'aurais pas dû le faire sinon) ; par
conséquent, l'optimizateur va virer des codes et autres choses sur la
base de cette inférence.


C'est ce que je m'attendrais d'un bon compilateur. (Encore que
ce n'est pas si évident à implémenter, si le compilateur utilise
un back-end et un optimisateur qui a été conçu pour C.)

J'ai l'impression que Microsoft fait exprès, et implémente le
support pour des références nulles comme une extension -- VC++
n'est pas complètement nul en ce qui concerne les optimisations,
et elle génère le code des tests même quand on initialise une
référence (de type Base) avec une autre (de type Derived). Reste
à savoir si c'est une extension officielle, ou simplement
quelque chose qu'ils ont implémenté en attendant virer les cas
qui en ont besoin dans leur propre code. S'ils n'ont pas écrit
quelque part qu'ils le supportent, il ne faut pas compter à ce
qu'ils le supportent dans VC++ 9, ou VC++ 10. (Certaines
mauvaises langues diraient que même s'ils ont écrit que ça
marche, il ne faut pas compter que ça marche dans la prochaine
version:-).)

--
James Kanze (GABI Software) email:
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

3 4 5 6 7