Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1082 connectés 

  FORUM HardWare.fr
  Programmation
  C++

  probleme de hierarchie de classes et copie profonde. [urgent]

 


 Mot :   Pseudo :  
 
 Page :   1  2
Page Précédente
Auteur Sujet :

probleme de hierarchie de classes et copie profonde. [urgent]

n°269894
karim63
Posté le 15-12-2002 à 03:32:08  profilanswer
 

bon g mis le code en bas , c pas propre y a des dechets etc.
Alors mon probleme ce trouve au niveau de la surcharge de l'operateur+ .
il y a la class entier dont derivent par heritage les entiers_court puis les entiers normaux, et les entiers longs.
Je voudrais que a+b renvoit un objet du type de a ou même b.
le probleme est que même si je créé un entier_court x avec a et b que je veux le renvoyer avec le return, vu que mon type de retour de la fonction est entier, et bien il me fait une espece de cast automatique et transforme le x en entier :heink: , mais moi je veux pas. :o
 
En fait y a pas moyen de selectionner le type de retour de la fonction. Moi je pensais qu'il m'enverrai l'objet x intact mais nan il fait une conversion du type de x.
Comment empecher cette conversion ?
Merci.
 

Code :
  1. #include <iostream.h>
  2. #include <typeinfo>
  3. #define ECS_MAX 127                 // max entier_court_signe
  4. #define ECS_MIN (-ECS_MAX-1)        // min entier_court_signe
  5. #define ES_MAX 32767                // max entier_signe
  6. #define ES_MIN (-ES_MAX-1)          // min entier_signe
  7. #define ELS_MAX 2147483647          // max entier_long_signe
  8. #define ELS_MIN (-ELS_MAX-1)        // min entier_long_signe
  9. #define ECNS_MAX 255                // max entier_court_non_signe
  10. #define ECNS_MIN 0                  // min entier_court_non_signe
  11. #define ENS_MAX 65535               // max entier_non_signe
  12. #define ENS_MIN 0                   // min entier_non_signe
  13. #define ELNS_MAX 4294967295UL       // max entier_long_non_signe
  14. #define ELNS_MIN 0                  // min entier_long_non_signe
  15. #define ES  entier_signe
  16. class erreur
  17. {
  18. public:
  19.   erreur(char* message){cout << endl << message << endl;};
  20. };
  21. // entier
  22. class entier
  23. {
  24. protected:
  25.   long long minimum;
  26.   long long maximum;
  27.   long long valeur;
  28. public:
  29.   entier(long long nombre=0,long long min=ELS_MIN,long long max=ELS_MAX)
  30.   {
  31.     valeur=nombre;
  32.    
  33.     minimum=min;
  34.     maximum=max;
  35.     if ( (nombre<min) || (nombre>max) ) throw erreur("depassement" );
  36.   };
  37.   entier(const entier &e)
  38.     {
  39.       valeur=e.valeur;
  40.       minimum=e.minimum;
  41.       maximum=e.maximum;
  42.      
  43.     };
  44.   virtual void bo(){ cout << "entier" <<" " << minimum << " " << maximum << endl;};
  45.  
  46.   /*
  47.   entier* operator+(entier* e)
  48.   {
  49.     typeof(*this)* x =new  typeof(*this) (this->valeur+e->valeur);
  50.          
  51.     return x;   
  52.   };
  53.   */
  54.   entier operator+(entier &e)
  55.   {
  56.     typeof(*this) x (this->valeur+e.valeur);
  57.     return x;
  58.   };
  59.   // cout << typeid(*px).name()<<" " << minimum << " " << maximum << endl;
  60.  
  61.   entier operator=(const entier & e)
  62.   {
  63.     typeof(*this) x (e.valeur);
  64.     //x.bo();
  65.    
  66.     return x;
  67.   };
  68.   friend ostream& operator<<(ostream& os,entier e){return os<<e.valeur;};
  69. };
  70. // signe
  71. class entier_long_signe:public entier
  72. {
  73. public:
  74.   entier_long_signe(long long nombre=0,long long min=ELS_MIN,long long max=ELS_MAX):entier(nombre,min,max){};
  75. virtual void bo(){ cout << "entier_long_signe" <<" " << minimum << " " << maximum << endl;};
  76. };
  77. class entier_signe:public entier_long_signe
  78. {
  79. public:
  80.   entier_signe(long long nombre=0,long long min=ES_MIN,long long max=ES_MAX):entier_long_signe(nombre,min,max){};
  81. virtual void bo(){ cout << "entier_signe" << " " << minimum << " " << maximum << endl;};
  82. };
  83. class entier_court_signe:public entier_signe
  84. {
  85. public:
  86.   entier_court_signe(long long nombre=0,long long min=ECS_MIN,long long max=ECS_MAX):entier_signe(nombre,min,max){};
  87.   virtual void bo(){ cout << "entier_court_signe" << " " << minimum << " " << maximum << endl;};
  88. };
  89. //non signe
  90. class entier_long_non_signe:public entier
  91. {
  92. public:
  93.   entier_long_non_signe(long long nombre=0,long long min=ELNS_MIN,long long max=ELNS_MAX):entier(nombre,min,max){};
  94. };
  95. class entier_non_signe:public entier_long_non_signe
  96. {
  97. public:
  98.   entier_non_signe(long long nombre=0,long long min=ENS_MIN,long long max=ENS_MAX):entier_long_non_signe(nombre,min,max){};
  99. };
  100. class entier_court_non_signe:public entier_non_signe
  101. {
  102. public:
  103.   entier_court_non_signe(long long int nombre=0,long long min=ECNS_MIN,long long max=ECNS_MAX):entier_non_signe(nombre,min,max){};
  104. }; 
  105. main()
  106. {
  107.   entier_court_signe a (50);
  108.   a.bo();
  109.   entier_court_signe b (10);
  110.   b.bo();
  111.   entier_signe c;
  112.   c=(a+b);
  113.   cout << a << " " << b << " " << (a+b) <<endl;
  114.   (a+b).bo();
  115.  
  116. }


Message édité par karim63 le 07-01-2003 à 23:55:47
mood
Publicité
Posté le 15-12-2002 à 03:32:08  profilanswer
 

n°269971
R3g
fonctionnaire certifié ITIL
Posté le 15-12-2002 à 13:08:42  profilanswer
 

Moi, j'aurais surchargé l'operateur+ dans chaque classe fille.
Ca fait plus de code, mais tu es sur de ton type de retour (bon en fait ca s'appelle contourner le probleme).

n°269975
karim63
Posté le 15-12-2002 à 13:25:02  profilanswer
 

R3g a écrit :

Moi, j'aurais surchargé l'operateur+ dans chaque classe fille.
Ca fait plus de code, mais tu es sur de ton type de retour (bon en fait ca s'appelle contourner le probleme).


 
Vi j'y ai pensé, mais je me disais que y avait ptere un autre moyen.

n°270030
nraynaud
lol
Posté le 15-12-2002 à 16:36:52  profilanswer
 

karim63 a écrit :

bon g mis le code en bas , c pas propre y a des dechets etc.
Alors mon probleme ce trouve au niveau de la surcharge de l'operateur+ .
il y a la class entier dont derivent par heritage les entiers_court puis les entiers normaux, et les entiers longs.
Je voudrais que a+b renvoit un objet du type de a ou même b.
le probleme est que même si je créé un entier_court x avec a et b que je veux le renvoyer avec le return, vu que mon type de retour de la fonction est entier, et bien il me fait une espece de cast automatique et transforme le x en entier :heink: , mais moi je veux pas. :o
 
En fait y a pas moyen de selectionner le type de retour de la fonction. Moi je pensais qu'il m'enverrai l'objet x intact mais nan il fait une conversion du type de x.
Comment empecher cette conversion ?
Merci.
 


 
ton typeof() est calculé statiquement, donc il prendra statiquement la valeur entier.
 
Tu as besoin du pattern Double-dispatch : tu dois répertir chaque classe sur une autre pour écrire les règles de calcul entre 2 instances de classes différentes.
 
tout d'abord, commence par extraire ton interface :
tu dégage une classe AbstractEntier
 
tu lui fout (AbstractEntier &) operator +(AbstractEntier &) et (AbstractEntier) operator =(AbstractEntier &)  et peut-être des méthodes d'accès au limites tout le monde en virtuel = 0 (méthodes abstraites).
 
ensuite, tu crées tes sous-classes distinctes, Entier, EntierLongSigne etc. (y'a peut-être moyen de dégager quelquechose par rapport à signé/non signé mais c'est pas le sujet).
 
Et c'est là que tu vas utiliser toutes la puissance de l'objet, mais comme le pattern en question je l'ai déjà écrit pour plein de monde (en plus pour ce cas-là !!), tu vas sur http://nraynaud.com.free.fr//td3dispatching.html et tu vas suivre les conseils (en traduisant le java en C++ donc toutes tes méthodes doivent être virtuelles, c'est là-dessus que repose tout le pattern).  
Et tu auras ainsi ton "vocabulaire de conception" d'un nouveau pattern natuerellement car tu avais un problème et ce pattern est là pour le résoudre.
 
edit :  
c'est sur que si t'es un gros flemmard, y'a des techinques de porc qui te permmetent de ne pas écrire n^2 méthodes (n étant le nombre de classes) mais dans ce cas, le principe ouvert-fermé tu te torches avec.


Message édité par nraynaud le 15-12-2002 à 17:31:56
n°270043
verdoux
And I'm still waiting
Posté le 15-12-2002 à 17:13:51  profilanswer
 

Avec le retour covariant du C++ (qui doit arriver dans java 1.5 je crois) il doit être possible de faire mieux non ?

n°270050
karim63
Posté le 15-12-2002 à 18:10:24  profilanswer
 

Citation :

ton typeof() est calculé statiquement, donc il prendra statiquement la valeur entier.


 
Benh ça je suis pas sur car j'ai mis des methodes pour tester, et x prend bien le type de l'objet this.
 
 

Citation :

Tu as besoin du pattern Double-dispatch : tu dois répertir chaque classe sur une autre pour écrire les règles de calcul entre 2 instances de classes différentes.
 
tout d'abord, commence par extraire ton interface :
tu dégage une classe AbstractEntier
 
tu lui fout (AbstractEntier &) operator +(AbstractEntier &) et (AbstractEntier) operator =(AbstractEntier &)  et peut-être des méthodes d'accès au limites tout le monde en virtuel = 0 (méthodes abstraites).
 
ensuite, tu crées tes sous-classes distinctes, Entier, EntierLongSigne etc. (y'a peut-être moyen de dégager quelquechose par rapport à signé/non signé mais c'est pas le sujet).
 
Et c'est là que tu vas utiliser toutes la puissance de l'objet, mais comme le pattern en question je l'ai déjà écrit pour plein de monde (en plus pour ce cas-là !!), tu vas sur http://nraynaud.com.free.fr//td3dispatching.html et tu vas suivre les conseils (en traduisant le java en C++ donc toutes tes méthodes doivent être virtuelles, c'est là-dessus que repose tout le pattern).  
Et tu auras ainsi ton "vocabulaire de conception" d'un nouveau pattern natuerellement car tu avais un problème et ce pattern est là pour le résoudre.
 
edit :  
c'est sur que si t'es un gros flemmard, y'a des techinques de porc qui te permmetent de ne pas écrire n^2 méthodes (n étant le nombre de classes) mais dans ce cas, le principe ouvert-fermé tu te torches avec.


 
hem g pas compris grandchose là  :whistle:
C'est pour un projet de licence info.
Je lis la page td3, je vais voir si ça m'aide.
Merci.

n°270085
nraynaud
lol
Posté le 15-12-2002 à 20:44:25  profilanswer
 

Et fout-moi du const un peu partout aussi, par ex, tu t'engage à pas modifier le paramètres du + mais à construire un nouvel objet pour le résultat. De même en règle générale, dans tout ce qui est maths on ne fait pas d'effets de bords (en tout cas rien qui se voit, un petit cache par-ci par là éventuellement, mais rien de visible pour l'utilisateur).

n°270097
karim63
Posté le 15-12-2002 à 21:27:30  profilanswer
 

nraynaud a écrit :

Et fout-moi du const un peu partout aussi, par ex, tu t'engage à pas modifier le paramètres du + mais à construire un nouvel objet pour le résultat. De même en règle générale, dans tout ce qui est maths on ne fait pas d'effets de bords (en tout cas rien qui se voit, un petit cache par-ci par là éventuellement, mais rien de visible pour l'utilisateur).


 
Comme dirait ma prof, il faut blinder  :D  :lol:  
Faudra qu'on m'explique un truc.  :heink:  
A partir du moment ou on sait que on ne va pas modifer l'objet passé en paramtre ça change rien si met const ou pas ... a part pour blinder   :whistle:  :lol:  
De même fo qu'on m'explique le "rien de visible pour l'utilisateur" je comprend pas ce que l'utilisateur peut voir si c'est compilé, mais bon  :jap:
 
Enfin j'ai rien compris au td3, et je suis un peu a la masse en java, mais bon je comprend pas vraiment là.

n°270102
lorill
Posté le 15-12-2002 à 21:38:16  profilanswer
 

karim63 a écrit :


De même fo qu'on m'explique le "rien de visible pour l'utilisateur" je comprend pas ce que l'utilisateur peut voir si c'est compilé, mais bon  :jap:


Utilisateur du composant (fonction, objet, ...) que tu ecrit... Ca peut être une autre partie du programme, pas forcément un être humain

n°270109
nraynaud
lol
Posté le 15-12-2002 à 22:06:47  profilanswer
 

karim63 a écrit :


 
Comme dirait ma prof, il faut blinder  :D  :lol:  


Oui enfin, je parlais de contrats, pas de conde défensif (blinder cétait pas clair).

Citation :


Faudra qu'on m'explique un truc.  :heink:  
A partir du moment ou on sait que on ne va pas modifer l'objet passé en paramtre ça change rien si met const ou pas ... a part pour blinder   :whistle:  :lol:  


Const "teinte" le paramètre passé en argument, tu ne pourras pas appeller sur lui des méthodes qui ne sount pas const et l'affecter à de variables qui ne sont pas const, affecter son adresse à des mointeurs qui ne sout pas const etc.
En gros, tu as la ganrantie de bout en bout que le paramètre ne sera pas modifé. Et ceci est vérifié par le compilateur, tu n'as donc pas de risque d'érreur humaine. Si tu le mets pas, tu risque de le passer en paramètre d'un truc qui au fin fond de la pile d'appel (après 15 appels de fontions et une distance parcourue énorme) va le modifier.
Tu peux ne pas le mettre mais déjà que le C++ ne fait pas grand'chose pour la qualité alors le minimum est d'utiliser ce qu'il y a.
 

Citation :


De même fo qu'on m'explique le "rien de visible pour l'utilisateur" je comprend pas ce que l'utilisateur peut voir si c'est compilé, mais bon  :jap:


L'utilisateur d'une classe est celui qui va l'instancier, en général, le gars qui n'a accès qu'à la doc et au .h. Lui doit savoir des truc et doit en ignorer d'autre, ceux qui sont suceptibles de changer, sur lesquel le concepteur ne s'engage pas etc.
S'il y a présence de caches par exemple, l'utilisateur n'a pas à le voir (par des paramètres bizarres, par des interfaces etc.).
 

Citation :


Enfin j'ai rien compris au td3, et je suis un peu a la masse en java, mais bon je comprend pas vraiment là.


 
Heu là si tu poses pas de questions précises, je vais réécrire ce qu'il y a dans le TD en C++ adapté à ton cas et ça ne va pas te servir à grand'chose.
 
Je tente de réexpliquer la démarche :
 
En gros le principe est de maîtriser les opérations entre 2 objets de type différent, pour le faire, il faut déjà décider pour chaque couple comment on va faire (caster qui vers qui, algorithme etc...). Comme tous ces types auront une interface commune (la possibilité de s'additionner entre eux etc.), on l'isole, d'où la présence d'un classe abstraite au plus haut  
niveau.
Ensuite, il faut créer les sous-classes concrètes qui vont te servir, toute héritières de la classe abstraite.
 
Tu créés dans chaque sous-classe les méthodes implantant les décisions prises au début : si je reçoit un objet de type machin et comme je suis moi-même de type bidule alors je calcule comme ça. C'est les méthodes du style :
 const AbstractEntier ajouterEntierLongSigne(const EntierLongSigne &bidule) const {...}
 
Tu en as nombre de classes^2 à écrire par opérateur (fait gaffe, c'est le double pour les non-commutatif, je t'en parle à la demaonde). Tu remontes enssuite leur prototype dans la classe abstraite, le principe de base est que tu _sais_ que tout le monde sait s'additionner avec tout le monde.
 
ensuite, il faut que la bonne méthode soit apellée à partir de (par ex.) +, c'est là qu'est la feinte d'apeller sur l'objet autre à droite du + (dont tu ne connais pas le type précis mais dont tu est sur qu'il sait t'additionner toi) la méthode autre.ajouterMaClasse(*this) car toi tu sais que tu est de type MaClasse et l'autre le saura car sinon tu n'aurait pas apellé cette fonction (et tous les types des paramètres à utiliser sont donnés dans le TD).
 
Bon, lis et reviens avec des questions.

mood
Publicité
Posté le 15-12-2002 à 22:06:47  profilanswer
 

n°270115
karim63
Posté le 15-12-2002 à 22:29:30  profilanswer
 

ok je vois mieux.
Mais en fait je pensais que y aurait moyen de s'en sortir sans devoir redefinir l'operateur+ dans chaque classe.
Disons que quand on renvoit un pointeur sur un objet, on peut renvoyer une objet derivée d'une classe de base bien que le type de retour soit cette classe de base. Dans ce cas, le type de l'objet retourné n'est psa modifé, sauf que pour que les methode des classes derviées soient appelées, il faut du virtual dans les classes superieurs.
Mais la apparement la conversion de l'objet de classe dérivée en la classe de base ce fait quand même, et ça je comprends pas.  :heink:  
Si l'objet retourné est plus specifique que le type retourné, et bien il doit pouvoir etre retourné tel quel vu que il contient toutes la carcteristiques de la classe de base. Comme c'est le cas lorsque l'on retourne un pointeur sur un objet.
 
Vois tu ce que je veux dire ?

n°270128
nraynaud
lol
Posté le 15-12-2002 à 23:54:14  profilanswer
 

karim63 a écrit :

ok je vois mieux.
Mais en fait je pensais que y aurait moyen de s'en sortir sans devoir redefinir l'operateur+ dans chaque classe.


tu peux redéfinir + uniquement dans le classe la plus haute et appeler une fonction plus(nb,nb) dedans si tu veux, mais c'est pareil (hormis que tu utilises le pattern template method).
 

Citation :


Disons que quand on renvoit un pointeur sur un objet, on peut renvoyer une objet derivée d'une classe de base bien que le type de retour soit cette classe de base. Dans ce cas, le type de l'objet retourné n'est psa modifé, sauf que pour que les methode des classes derviées soient appelées, il faut du virtual dans les classes superieurs.
Mais la apparement la conversion de l'objet de classe dérivée en la classe de base ce fait quand même, et ça je comprends pas.  :heink:  


Je ne pense pas qu'il y ait conversion réelle, tout simplement car le compilo n'aucune idée de comment passer de l'un à l'autre (et moi non plus d'ailleur) par contre, le contenu de ta variable sera _vu_ comme s'il était de la classe de base. ça rejoint la remarque de lorill ici :  
http://forum.hardware.fr/forum2.ph [...] h=&subcat=
 

Citation :


Si l'objet retourné est plus specifique que le type retourné, et bien il doit pouvoir etre retourné tel quel vu que il contient toutes la carcteristiques de la classe de base. Comme c'est le cas lorsque l'on retourne un pointeur sur un objet.
 
Vois tu ce que je veux dire ?


C'est retourné tel quel, je ne vois pas le problème.
 
Je viens de relire ton post original, c'est des types ancrés que tu veux faire, ça n'existe pas en C++ (on peut approcher en bidouillant avec typeof mais on est très loin encore), uniquement en Eiffel à ma conaissance.

n°270140
karim63
Posté le 16-12-2002 à 00:40:57  profilanswer
 

bon benh si c'est retourné tel kel, et bien je ne comprend pas pourquoi l'objet retourné même si il est du type entier_court_signe par exemple, et bien apres etre passé dans le return, c'est un entier, et même ses bornes mnimum et maximum qui sont censé rester a -128 127 et bien ce retrouvent avec les valeurs 2147483647 ,  -2147483648 qui sont les valeurs par defaut si on appelle le constructeur entier. (ok c pas possible il devrait etre abstrait et en plus les bornes sont fausses ou du moins n'ont pas de signification vu que c'est censé etre une classe abstraite).
C'est donc pour ça que j'ai fait la methode bo() pour comprendre ce qui ce passait en effet cette methode renvoit le type de l'objet.
 
 
en fait ce qui me fait halluciner c'est que ce qu'il me renvoit, c'est comme si il construisait un entier de cette façon : entier bidule (valeur)
donc même si je retourne un entier_court_signe sur le type entier, il le transforme entier par je ne sait kel miracle, et lui donne la bonne valeur numerique mais plu le bon type.
Tu n'as pas l'air convaincu par cela mais pourtant c'est ce qui ce passe et j'en suis le premier etonné.
Peut etre que c'est le constructeur d'affectation par defaut qui entraine cela.  :??:  
 
Un autre truc qui m'a vraiment bluffé, c'est que je n'ai pas surchargé = et pourtant quand je fait :  

Code :
  1. entier_court_signé a (22);
  2. a=24;


et bien le champ valeur de l'objet a vaut 24  :heink:  
 :??:  
 
Merci pour ton aide.  :jap:  

n°270146
nraynaud
lol
Posté le 16-12-2002 à 01:10:00  profilanswer
 

karim63 a écrit :

bon benh si c'est retourné tel kel, et bien je ne comprend pas pourquoi l'objet retourné même si il est du type entier_court_signe par exemple, et bien apres etre passé dans le return, c'est un entier, et même ses bornes mnimum et maximum qui sont censé rester a -128 127 et bien ce retrouvent avec les valeurs 2147483647 ,  -2147483648 qui sont les valeurs par defaut si on appelle le constructeur entier. (ok c pas possible il devrait etre abstrait et en plus les bornes sont fausses ou du moins n'ont pas de signification vu que c'est censé etre une classe abstraite).
C'est donc pour ça que j'ai fait la methode bo() pour comprendre ce qui ce passait en effet cette methode renvoit le type de l'objet.
 
 
 
Un autre truc qui m'a vraiment bluffé, c'est que je n'ai pas surchargé = et pourtant quand je fait :  

Code :
  1. entier_court_signé a (22);
  2. a=24;


et bien le champ valeur de l'objet a vaut 24  :heink:  
 :??:  
 
Merci pour ton aide.  :jap:  
 


Tu devrais vraiment commencer à concevoir proprement, virer tout code effectif de la classe du haut. Tu te posera moins de questions.

n°270152
Musaran
Cerveaulté
Posté le 16-12-2002 à 05:06:13  profilanswer
 

verdoux a écrit :

Avec le retour covariant du C++ (qui doit arriver dans java 1.5 je crois) il doit être possible de faire mieux non ?

Ça ne marche que par référence/pointeur, et là il faut qu'on retourne un objet.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°270153
Musaran
Cerveaulté
Posté le 16-12-2002 à 05:08:06  profilanswer
 

Il faudrait penser aussi à un truc qui s'apelle le standard...

Code :
  1. #include <iostream.h> //<iostream>+using
  2. long long minimum; //pas encore dispo en C++
  3. typeof(); //pas encore dispo
  4. main() //int main()


 

Code :
  1. #define ECS_MAX 127                // max entier_court_signe

Quand à ça...
D'abord les constantes remplacent les defines en C++.
Ensuite, pas la peine d'inventer ces nombres, ils sont fournis:
numeric_limits<signed char>::max()
 
 
Bon, pour ton problème je propose simplement un opérateur binaire séparé:

Code :
  1. #include <iostream>
  2. #include <typeinfo>
  3. using namespace std;
  4. #define ECS_MAX 127                // max entier_court_signe
  5. #define ECS_MIN (-ECS_MAX-1)       // min entier_court_signe
  6. #define ES_MAX 32767               // max entier_signe
  7. #define ES_MIN (-ES_MAX-1)         // min entier_signe
  8. #define ELS_MAX 2147483647         // max entier_long_signe
  9. #define ELS_MIN (-ELS_MAX-1)       // min entier_long_signe
  10. class erreur{
  11. public:
  12. erreur(char* message){cout << endl << message << endl;};
  13. };
  14. class entier{
  15. protected:
  16. long minimum;
  17. long maximum;
  18. long valeur;
  19. public:
  20. entier(long nombre=0,long min=ELS_MIN,long max=ELS_MAX){
  21.  valeur=nombre;
  22.  minimum=min;
  23.  maximum=max;
  24.  if ( (nombre<min) || (nombre>max) ) throw erreur("depassement" );
  25. }
  26. entier(const entier &e){
  27.  valeur=e.valeur;
  28.  minimum=e.minimum;
  29.  maximum=e.maximum;
  30. }
  31. entier operator=(const entier & e){
  32.  return valeur= e.valeur; //ajouter tests
  33. }
  34. void bo(){ cout << typeid(*this).name() << " " << minimum << " " << maximum << endl;}
  35. friend entier operator+(entier&, entier&){return lhs.valeur + rhs.valeur;};
  36. friend ostream& operator<<(ostream& os,entier e){return os<<e.valeur;};
  37. };
  38. class entier_signe:public entier_long_signe{
  39. public:
  40. entier_signe operator=(const entier_signe & e){
  41.  return this->entier_long_signe::operator=(e);
  42. };
  43. entier_signe(const entier& e):entier_long_signe(e){};
  44. entier_signe(long nombre=0,long min=ES_MIN,long max=ES_MAX):entier_long_signe(nombre,min,max){};
  45. }; 
  46. class entier_court_signe:public entier_signe{
  47. public:
  48. entier_court_signe operator=(const entier_court_signe & e){
  49.  return this->entier_signe::operator=(e);
  50. };
  51. entier_court_signe(const entier& e):entier_signe(e){};
  52. entier_court_signe(long nombre=0,long min=ECS_MIN,long max=ECS_MAX):entier_signe(nombre,min,max){};
  53. };
  54. int main(){
  55. entier_court_signe a (50); a.bo();
  56. entier_court_signe b (10); b.bo();
  57. entier_signe c;
  58. c= (a+b);
  59. (a+b).bo();
  60. return 0;
  61. }

Je n'ai pas pu tester complètement (mon compilateur me vomit 'INTERNAL COMPILER ERROR' sans raisons).
 
Je crois qu'il y a moyen avec les patrons aussi, mais adieu la hiérarchie.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°270372
karim63
Posté le 16-12-2002 à 12:59:47  profilanswer
 

Code :
  1. entier_court_signe(const entier& e):entier_signe(e){};


le sert a koi cette ligne ?
 
sinon au niveau du codage propre fo pas trop en demander, car ça fait 2 mois que je connais c++ et j'avais jamais fait de c avant. C'est une incoherence de la fac.

n°270482
nraynaud
lol
Posté le 16-12-2002 à 15:19:20  profilanswer
 

karim63 a écrit :

Code :
  1. entier_court_signe(const entier& e):entier_signe(e){};


le sert a koi cette ligne ?
 
sinon au niveau du codage propre fo pas trop en demander, car ça fait 2 mois que je connais c++ et j'avais jamais fait de c avant. C'est une incoherence de la fac.


 
elle rappelle le constructeur du dessus pour initialiser les champs que tu serais incapable d'initialiser en bas.
la même notation sert à initialiser un champ de ta classe :
 

Code :
  1. class A {
  2. int instvar;
  3. A(int v):instvar(v){}
  4. }


 
à quoi ça sert puisque l'affectation peut le faire ?

Code :
  1. class A {
  2. const int instvar;
  3. A(int v):instvar(v){}
  4. }


 
et ben voilà une partie de la réponse.
 
sinon, cette notation n'appelle pas l'opérateur = donc s'il est surchargé par un truc super lent, il ne sera pas appelé (on serait bine dans la merde avec un this->instvar qui n'a pas de valeur définie).
 
donc notation initialisation, différente de la notation affectation. Elle permet d'appeler les constructeurs du dessus (qui vont initialiser les variables d'instance définies au dessus) et les variables d'instance de cette classe.
 
Voilà, une notation de plus avec sa sémantique, tu progresses sur la route du C++ (beuark).
 
Sinon, je suis assez pour faire apprendre aux gens le C++ sans prendre les mauvaises habitudes du C d'abord. Il faut y aller progressivement (ce qui sembe ton cas vu que tu ne maîtrises pas encore const et la notation initialisation), c'est tout.
Par contre, je préfèrerais faire apprendre l'objet sur un langage où la grammaire n'est pas un problème : smalltalk. Il n'y a rien commme notations vicieuse là dedans, et tout est à l'exécution, on se concentre sur l'essentiel : la conception objet.

n°270953
karim63
Posté le 16-12-2002 à 20:09:02  profilanswer
 

Il ne faut pas trop etre exigent sur le plan coherence pedagogique en fac.

n°271108
Musaran
Cerveaulté
Posté le 17-12-2002 à 06:06:47  profilanswer
 

C'est clair que la syntaxe du C++ est... spéciale (soyons gentils).
Si le but est d'apprendre l'objet, il n'est pas 'pur' non plus.
Je pense qu'on peut faire le C avant le C++, mais que ce n'est vraiment pas la peine de s'y attarder.
 
Bref, malgré des défauts encombrants j'aime le C++ pour sa puissance d'expression.
 
 

Code :
  1. class entier{
  2. void bo(){ cout << typeid(*this).name() << " " << minimum << " " << maximum << endl;}
  3. }

Je me suis trompé en mettant ça, le typage reste statique: 'entier'.
Il faut bien garder une fonction virtuelle:

Code :
  1. class entier{
  2. virtual const char* getName(){return "entier";}
  3. void bo(){ cout << getname() << " " << minimum << " " << maximum << endl;}
  4. }


Ou alors il existe une feinte ?


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°271752
nraynaud
lol
Posté le 17-12-2002 à 19:30:54  profilanswer
 

Musaran a écrit :

Code :
  1. class entier{
  2. virtual const char* getName(){return "entier";}
  3. void bo(){ cout << getname() << " " << minimum << " " << maximum << endl;}
  4. }


Ou alors il existe une feinte ?


 
Pourquoi ? c'est de l'objet, c'est propre (ça repose sur la liaison retardé), c'est un pattern connu (template method), je ne pense pas qu'il y ait mieux (en dehors de virer la surcharge de << mais c'est pas ta question).
 
Par contre la hiérarchie aïe ! Elle passe pas 10s à l'écriture du contrat. Ni aux métriques de Martin (ségrégation des interface, inversion des dépendances).

n°271907
Musaran
Cerveaulté
Posté le 18-12-2002 à 05:53:46  profilanswer
 

Je m'étonne simplement que quand on lui passe un objet, typeid() ne regarde pas son type dynamique (s'il existe).
Ça y est, je viens de comprendre... j'avais enlevé toute fonction virtuelles, donc adieu le RTTI pour entier.
Il suffit donc de mettre une fonction virtuelle de recherche de nom pour qu'on n'en ait plus besoin !
 
Voilà, c'était le truc idiot du jour...
 

Citation :

Par contre la hiérarchie aïe ! Elle passe pas 10s à l'écriture du contrat. Ni aux métriques de Martin (ségrégation des interface, inversion des dépendances).

Tching thang thoung ?
Pour répondre à ça, il va falloir que je passe un peu de temps à potasser les termes POO.
 
Mais le code que j'ai balancé, c'est juste un début de correction, je n'aurais pas fait comme ça de toutes façons.


---------------
Bricocheap: Montage de ventilo sur paté de mastic silicone
n°272120
nraynaud
lol
Posté le 18-12-2002 à 13:17:37  profilanswer
 

Musaran a écrit :

Je m'étonne simplement que quand on lui passe un objet, typeid() ne regarde pas son type dynamique (s'il existe).
Ça y est, je viens de comprendre... j'avais enlevé toute fonction virtuelles, donc adieu le RTTI pour entier.
Il suffit donc de mettre une fonction virtuelle de recherche de nom pour qu'on n'en ait plus besoin !
 
Voilà, c'était le truc idiot du jour...


 
Puis-je juste te suggérer de ne jammais utiliser ça en dehors du débogage, dignostique d'erreur ou exploration (si ça existe en C++, jamais vu avant smalltalk) ou de menaces de mort crédibles ?  
 

Citation :


 il va falloir que je passe un peu de temps à potasser les termes POO.


 
tiens, pour t'aider :  
http://www.objectmentor.com/resour [...] .%20Martin
Les publications de Robert C. Martin, j'ai tout lu mais malheureusement, tout n'est pas intégré (en gros je suis pas satisfait de mon code).
 
 
Pour les contrats, la conception et la qualité, le meilleur livre de l'univers :
http://www.eyrolles.com/php.inform [...] 2212091113
c'est 60 euros _très_ bien investis.
 
 

n°272308
karim63
Posté le 18-12-2002 à 18:50:28  profilanswer
 

autrement g une question.
J'avfais tenté de surcharger l'operateur+ mais en amis.
De cette maniere je pouvais faire entier+entier.
J'ai fait ça :
 

Code :
  1. entier* operator+(entier* a,entier* b)
  2.   {
  3.     typeof(*a)* x  = new typeof(*a) (a->valeur+b->valeur);
  4.     return x;
  5.   };


 
avec dans le main ça par exemple et aussi diferentes modifs dans le prog :
 

Code :
  1. entier_court_signe* a= new entier_court_signe (50);
  2. entier_court_signe* b= new entier_court_signe (60);


 
Sauf que operator+ veut pas des pointeurs  :??:  :sweat:  

n°272313
nraynaud
lol
Posté le 18-12-2002 à 18:53:08  profilanswer
 

karim63 a écrit :

autrement g une question.
J'avfais tenté de surcharger l'operateur+ mais en amis.
De cette maniere je pouvais faire entier+entier.
J'ai fait ça :
 

Code :
  1. entier* operator+(entier* a,entier* b)
  2.   {
  3.     typeof(*a)* x  = new typeof(*a) (a->valeur+b->valeur);
  4.     return x;
  5.   };


 
avec dans le main ça par exemple et aussi diferentes modifs dans le prog :
 

Code :
  1. entier_court_signe* a= new entier_court_signe (50);
  2. entier_court_signe* b= new entier_court_signe (60);


 
Sauf que operator+ veut pas des pointeurs  :??:  :sweat:  
 


 
ET SI avant que quelqu'un ne réponde à ta question, tu commançais par oublier l'existance de l'opérateur typeof() ?
 
Tu aurais fait un grand pas vers la connaissance !

n°272319
karim63
Posté le 18-12-2002 à 18:55:45  profilanswer
 

Code :
  1. `operator +(entier *, entier *)' must have an argument of class or enumerated type

n°272327
karim63
Posté le 18-12-2002 à 19:00:12  profilanswer
 

nraynaud a écrit :


 
ET SI avant que quelqu'un ne réponde à ta question, tu commançais par oublier l'existance de l'opérateur typeof() ?
 
Tu aurais fait un grand pas vers la connaissance !
 


 
mouerf.
Mais bon oublions le type of.
Ici ce que je veux souligner c'est que apparement operator+ veut pas de pointeurs en parametres, je vais pas faire un autre topic pour ce "probleme". Ici je veux juste savoir pourquoi on peut pas mettre de pointeurs sur le +.
Sinon c koi le prob avec le typeof ? Etant donné que je voudrais creer un objet du type d'un des deux passé en params comment faire autrement ?
Franchement t'haluccinerais si tu voyais les bidouilles des autres personnes  :lol: .

n°272330
nraynaud
lol
Posté le 18-12-2002 à 19:00:51  profilanswer
 

karim63 a écrit :

Code :
  1. `operator +(entier *, entier *)' must have an argument of class or enumerated type




 
normal, la syntaxe c'est :  

Code :
  1. `operator +(entier , entier )' must have an argument of class or enumerated type


 
le mode normal qui recopie sur la pile ou :

Code :
  1. `operator +(entier& , entier& )' must have an argument of class or enumerated type


 
le passage par référence (qui s'utilise avec un point).
 
Come ton niveau monte, utilise const :

Code :
  1. `operator +(const entier& , const entier& )' must have an argument of class or enumerated type


 
pour dire "je m'engage à ne pas modifier mes entrées".

n°272333
karim63
Posté le 18-12-2002 à 19:05:51  profilanswer
 

nraynaud a écrit :


Come ton niveau monte, utilise const :

Code :
  1. `operator +(const entier& , const entier& )' must have an argument of class or enumerated type


 
pour dire "je m'engage à ne pas modifier mes entrées".


 
Attend tu ne me parles pas sur ce ton là s'il te plait.  :D  
Je viens d'avoir 15 a mon CC d'objet   :wahoo:
 
 ;)

n°272339
karim63
Posté le 18-12-2002 à 19:12:23  profilanswer
 

nraynaud a écrit :


 
normal, la syntaxe c'est :  

Code :
  1. `operator +(entier , entier )' must have an argument of class or enumerated type


 
le mode normal qui recopie sur la pile ou :

Code :
  1. `operator +(entier& , entier& )' must have an argument of class or enumerated type


 
le passage par référence (qui s'utilise avec un point).
 
Come ton niveau monte, utilise const :

Code :
  1. `operator +(const entier& , const entier& )' must have an argument of class or enumerated type


 
pour dire "je m'engage à ne pas modifier mes entrées".


 
Mais ça marche pas ce que tu m'a donné. :heink:  
Ici ce que je voulais c'est que operator+  prenne en parametre des pointeurs sur des entiers et renvoit un pointeur sur un entier et pas prendre des objets entier et renvoyer un entier.
(je sais ça risque d'etre tres porc pour supprimer les objets mais bon  :whistle: ).

n°272340
nraynaud
lol
Posté le 18-12-2002 à 19:13:13  profilanswer
 

karim63 a écrit :


 
Attend tu ne me parles pas sur ce ton là s'il te plait.  :D  
Je viens d'avoir 15 a mon CC d'objet   :wahoo:
 
 ;)  


 
Tu veux que je te présentes mon ancien prof d'objet ? il nous filait comme exemple :
 

Code :
  1. class point2D {
  2. ...
  3. }
  4. class point3D: public point2D {
  5. ...
  6. }


 
(en smalltalk).
 
Donc avoir 18 à son interro c'est pas dur !
ça ne veut absolument rien dire par rapport à ton niveau réel.

n°272350
karim63
Posté le 18-12-2002 à 19:35:03  profilanswer
 

nraynaud a écrit :


 
Tu veux que je te présentes mon ancien prof d'objet ? il nous filait comme exemple :
 

Code :
  1. class point2D {
  2. ...
  3. }
  4. class point3D: public point2D {
  5. ...
  6. }


 
(en smalltalk).
 
Donc avoir 18 à son interro c'est pas dur !
ça ne veut absolument rien dire par rapport à ton niveau réel.


 
Je fait du c++ depuis 2-3 mois, tout est relatif.
C'etait pas tres dur non plus.
http://perso.wanadoo.fr/mirak63/images/ccul41.GIF
http://perso.wanadoo.fr/mirak63/images/ccul42.GIF
 
Sinon tu n'as pas repondu a ma question, pkoi c pas possible de mettre des pointeurs en parametre dans operator+ ?

n°272373
nraynaud
lol
Posté le 18-12-2002 à 20:19:52  profilanswer
 

karim63 a écrit :


 
Je fait du c++ depuis 2-3 mois, tout est relatif.
C'etait pas tres dur non plus.
http://perso.wanadoo.fr/mirak63/images/ccul41.GIF
http://perso.wanadoo.fr/mirak63/images/ccul42.GIF
 
Sinon tu n'as pas repondu a ma question, pkoi c pas possible de mettre des pointeurs en parametre dans operator+ ?


 
parce que pour l'utiliser, il faudrait que tu écrives :
 

Code :
  1. int i1 = 2;
  2. int i2 = 3;
  3. int *r;
  4. int * operator +(const int *l , const int *r ){
  5.    int *r = malloc(sizeof(int));
  6.    *r = *l + *r;
  7.    return r;
  8. }
  9. int main(void) {
  10.   r := &i1 + &i2;
  11. }

 
 
et ça devrait marcher mais c'est hyper lourd.
 
alors que, à l'utilisation, les références sont prises et déréférencées sans que tu n'aie de syntaxe particulière.

n°272380
karim63
Posté le 18-12-2002 à 20:31:26  profilanswer
 

Code :
  1. int * operator +(const int *l , const int *r )
  2. {
  3.   int *r = malloc(sizeof(int));
  4.   *r = *l + *r;
  5.   return r;
  6. }
  7. int main(void)
  8. {
  9.   int i1 = 2;
  10.   int i2 = 3;
  11.  
  12.   int *r;
  13.  
  14.   r = &i1 + &i2;
  15. }


 

Citation :

test.cpp:7: `operator +(const int *, const int *)' must have an argument of class or enumerated typetest.cpp: In function `int * operator +(const int *, const int *)':
test.cpp:8: declaration of `r' shadows a parameter
test.cpp:8: implicit declaration of function `int malloc(...)'
test.cpp:8: initialization to `int *' from `int' lacks a cast
test.cpp: In function `int main()':
test.cpp:20: invalid operands `int *' and `int *' to binary `operator +'


 
bah non ça marche pas.
Ormis d'autres erreurs, la premiere erreur a la ligne en grad est la même.
Je ne m'y connait pas vraiment mais je trouve ça surprenant et dommage.


Message édité par karim63 le 18-12-2002 à 20:35:10
n°272386
nraynaud
lol
Posté le 18-12-2002 à 20:41:59  profilanswer
 

karim63 a écrit :

Code :
  1. int * operator +(const int *l , const int *r )
  2. {
  3.   int *r = malloc(sizeof(int));
  4.   *r = *l + *r;
  5.   return r;
  6. }
  7. int main(void)
  8. {
  9.   int i1 = 2;
  10.   int i2 = 3;
  11.  
  12.   int *r;
  13.  
  14.   r = &i1 + &i2;
  15. }


 

Citation :

test.cpp:7: `operator +(const int *, const int *)' must have an argument of class or enumerated typetest.cpp: In function `int * operator +(const int *, const int *)':
test.cpp:8: declaration of `r' shadows a parameter
test.cpp:8: implicit declaration of function `int malloc(...)'
test.cpp:8: initialization to `int *' from `int' lacks a cast
test.cpp: In function `int main()':
test.cpp:20: invalid operands `int *' and `int *' to binary `operator +'


 
bah non ça marche pas.
Ormis d'autres erreurs, la premiere erreur a la ligne en grad est la même.
Je ne m'y connait pas vraiment mais je trouve ça surprenant et dommage.  


 
Va vraiment falloir que j'arrête la drogue !
C'est évident que ça pouvait pas marcher : l'addition de pointeurs n'a aucun sens, c'est l'addition d'un pointeur et d'un entier qui en a (accéder à l'élément de rang n après le pointeur).

n°272389
karim63
Posté le 18-12-2002 à 20:56:03  profilanswer
 

nraynaud a écrit :


 
Va vraiment falloir que j'arrête la drogue !
C'est évident que ça pouvait pas marcher : l'addition de pointeurs n'a aucun sens, c'est l'addition d'un pointeur et d'un entier qui en a (accéder à l'élément de rang n après le pointeur).
 


 

Code :
  1. r := &i1 + &i2;


 
Cette ligne est assez marante.
Sinon ok l'addition de deux pointeurs a pas de sens. Dans le sens ou effectivement on peut additioner un nombre a un pointeur ou l'incrementer.
Je suis tout a fait d'accord que additioner les valeurs de deux adresses a pas de sens.
Par contre l'addition de deux objets designés par leur pointeur a un sens !
L'addition de leur contenu a un sens. Sauf que là c pas possible :/ .
Le probleme vient du fait que on pourait croire que c'est ce qu'on veut faire, mais non ce qu'on veut faire des pointeurs nous regarde  :fou: .
A partir du momment ou on sait que additioner deux pointeurs (les adresses) est ridicule il suffirait qu'il implmeente pas ce prototpe opertor+(x*,x*) et nous permettre de le faire si on veut. Je vois pas pourquoi par contre on peut pas le surdefinir. Techniquement rien ne l'empecherait.
Je dois pas comprendre un truc.  :o  
 
D'ailleur je me demandais, est qu'on peut surdefinir operator+(int,int) ?
 
Par exemple :
int operator+(const int &a,const int &b)
{
return a*b-1;
};
 
:whistle:
 
bon ok je sors  :lol:  

n°272392
nraynaud
lol
Posté le 18-12-2002 à 20:59:48  profilanswer
 

karim63 a écrit :


 

Code :
  1. r := &i1 + &i2;


 
Cette ligne est assez marante.
Sinon ok l'addition de deux pointeurs a pas de sens. Dans le sens ou effectivement on peut additioner un nombre a un pointeur ou l'incrementer.
Je suis tout a fait d'accord que additioner les valeurs de deux adresses a pas de sens.
Par contre l'addition de deux objets designés par leur pointeur a un sens !
L'addition de leur contenu a un sens. Sauf que là c pas possible :/ .


oui et ça sécrit :
 
*i + *j  
 
 

Citation :


Le probleme vient du fait que on pourait croire que c'est ce qu'on veut faire, mais non ce qu'on veut faire des pointeurs nous regarde  :fou: .
A partir du momment ou on sait que additioner deux pointeurs (les adresses) est ridicule il suffirait qu'il implmeente pas ce prototpe opertor+(x*,x*) et nous permettre de le faire si on veut. Je vois pas pourquoi par contre on peut pas le surdefinir. Techniquement rien ne l'empecherait.
Je dois pas comprendre un truc.  :o  


 
ben simplement, ajouter l'adresse de 2 cases mémoire n'a pas de sens donc c'est pas possible.
 

Citation :


D'ailleur je me demandais, est qu'on peut surdefinir operator+(int,int) ?
 
Par exemple :
int operator+(const int &a,const int &b)
{
return a*b-1;
};
 
:whistle:
 
bon ok je sors  :lol:  


 
je suppose que oui, teste !

n°272409
karim63
Posté le 18-12-2002 à 21:19:30  profilanswer
 

nraynaud a écrit :


ben simplement, ajouter l'adresse de 2 cases mémoire n'a pas de sens donc c'est pas possible.


 
oui mais ajouter deux objets commençant a tel case memoire devrait avec nos propres regles ça devrait etre possible. D'autant plus qu'on connait le type.bref ... Ca aurait simplifié les choses.

n°272449
nraynaud
lol
Posté le 18-12-2002 à 22:21:01  profilanswer
 

karim63 a écrit :


 
oui mais ajouter deux objets commençant a tel case memoire devrait avec nos propres regles ça devrait etre possible. D'autant plus qu'on connait le type.bref ... Ca aurait simplifié les choses.


 
Le principe de la surcharge n'est pas de modifier le comportement d'un opérateur mais d'adapter son comportement à une réalité qui a un peu changé. Si "de base" ça fait un truc, sur un truc plus évolué, ça doit faire _le même_ de la façon adaptée. Le but n'est pas de changer le comportement des opérateur (préservation du contrat). Donc si "de base" ça n'a pas de sens, tu n'a pas de droit de lui en donner un, tu changerais son sens.

n°272454
karim63
Posté le 18-12-2002 à 22:33:36  profilanswer
 

c'est ridicule.
Si un objet peut etre identifié a travers son pointeur, dans ce cas y a pas de raison qu'on puisse pas ce servir de la surcharge du + pour additioner deux objets au travers du pointeur.
Ca eviterait de devoir faire *a+*b et en plus ça permettrait de faire facilement ce que j'essayait de faire dans mon prog.
Dans la mesure ou l'adddition des adresses contenue dans deux pointeurs est pas autorisée, je vois pas pkoi les concepteurs ne ce sont pas gardé la possibilité d'additioner deux objets en ne connaissant que leurs pointeurs et leur type. Ca me semble logique ou du moins allant de soit. Dans la mesure ou ça aporte d'autre possiblité je vois pkoi faudrait s'en priver.
le contrat et tout ça c'est du blabla je trouve quand on voit a quel point le c++ est quand même un langage de bidouille.  :jap:

mood
Publicité
Posté le   profilanswer
 

 Page :   1  2
Page Précédente

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C++

  probleme de hierarchie de classes et copie profonde. [urgent]

 

Sujets relatifs
Cherche WEBMASTER URGENT !!!!Problème sur un devoir en assembleur [Help : c'est pas simple <:'o( ]
[ Les Classes et la POO ] quelqu un dans la salle ?Assez Urgent : Scrolling de frame
question bete : probleme dans la déclaration d'un tableau[ACCESS] Problème de requette
Requête SQL sur controle ADO - Problème[JAVA] Problème avec javax.servlet :/
[JS] Petit probleme de retour à la window par défaut apres un popup[ASP] Pb d'affichage des zones de texte [URGENT]
Plus de sujets relatifs à : probleme de hierarchie de classes et copie profonde. [urgent]


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR