IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

héritage / transtypage


Sujet :

C++

  1. #21
    Membre régulier

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    133
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 133
    Points : 113
    Points
    113
    Par défaut
    Citation Envoyé par buzzkaido Voir le message
    dans une de mes fonctions je copiais une instance de P avec un P* p=new P;
    Pour recopier un objet proprement, il te faut déclarer un constructeur de recopie, c'est beaucoup plus sûr.

    P* p=(P*)malloc(sizeof(q)); puis memcpy
    C'est sûr qu'en théorie ça marche, mais en pratique, c'est risqué !!

    Parceque quand tu fais ça, aucun constructeur n'est appaellé, donc aucun membre n'est initialisé, et du coup, l'appel à des méthodes sur cet objet peut donner tout et n'importe quoi.
    coucou

    tu es sûr que ce code n'est pas fiable ? quel est le problème au juste ? dans quel cas ça ne marche pas ? et sinon, comment faire un constructeur par recopie pour une classe abstraite ?

    ça fait 2 jours que je code comme un malade, si jamais il y a un bug dans ça je me pend direct lol, plus la force mentale de tout recommencer

    en tout cas merci pour vos réponses qui sont très intéressantes !

  2. #22
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    En fait, c'est surtout le malloc qui n'est pas fiable

    L'idéal est, en effet, de prévoir un constructeur par copie, qui fait d'ailleurs partie des "automatics four"

    En effet, le compilateur va fournir quatre éléments à toute classe déclarée, à moins qu'il ne se rende compte qu'ils ont été définis (ou du moins déclarés):
    • un constructeur par défaut (ne prenant aucun argument): MaClass() si aucun constructeur n'est défini (qu'il prenne ou non un ou des argument(s)
    • Un destructeur: ~Maclass()
    • Un constructeur par copie MaClass(const MaClass&)
    • un opérateur d'assignation: MaClass operator=(const MaClass&)

    L'astuce est qu'ils ne seront réellement efficace que si tous les membres sont des objets "simples", et, surtout pas des pointeurs

    Ainsi, tu peux faire confiance au compilateur pour une classe du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class LaClassOK
    {
        public:
            LaClassOK(int i=0, int j=0; int k=0):i(i),j(j),k(k){}
            /*...*/
        private:
            int i;
            int j;
            int k;
    };
    Et cela fonctionnerait d'ailleurs tout aussi bien si, au lieu d'utiliser des int, tu utilisais un type personnalisé MonType, car tu travailles directement sur des instances d'objet, et que le constructeur par copie et l'opérateur d'assignation feront des copies "membre à membre" du contenu de ta classe

    Par contre, là où cela coince, c'est dans le cadre d'une classe du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class LaClassBobo
    {
        public:
            LaClassBobo(MonType* m):m(m){}
            ~LaClassBobo(){delete m;}
            /*...*/
        private:
            MonType* m;
    };
    car toutes les copies verront leur "m" pointer vers... la même adresse mémoire

    Du coup, tu n'auras pas de problème tant que toutes les copie de l'objet existeront encore... Mais dés que tu sortira d'une portée dans laquelle une seule de ces copies est déclarée, cette copie sera détruite... et provoquera l'invalidation de l'adresse pointée par m

    De ce fait, la première fois où, après cette destruction, tu tenterais d'accéder à m (ou à ce qui est pointé par m), tu te retrouvera confronté à une erreur de segmentation... dans le meilleur des cas

  3. #23
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Je voudrais aussi revenir sur quelques infos qui semblent relativement mal comprises:

    Les fonctions "inline" pour commencer:

    Le fait de définir une fonction inline, de manière implicite ou explicite, ne fait que demander au compilateur de l'inliner... Mais il reste tout à fait libre de le faire... ou non
    1. Il sera, par exemple, tout à fait incapable d'inliner une fonction (qui travaillerait sous un mode de) récursive
      Combien de fois devrait-il recopier le code 3fois 5 fois 150 fois ... Quel que soit le nombre de fois qu'il la copierait, on pourrait se trouver dans la situation qu'il faudrait N+1 appels à cette fonction
    2. Une fonction peut ne pas sembler récursive de prime abord, et pourtant, dans le code
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
       
      class MaClass
      {
          public:
              MaClass();
              ~MaClass()
              {
                  for(size_t i=0;i<tab.size();i++)
                      delete tab[i];
                  tab.clear();
              }
              /*...*/
          private:
              std::vector<MaClass*> tab;
      };
      le destructeur fonctione bel et bien sur le mode récursif
    3. l'idée même d'une fonction virtuelle, avec le problème de vtable que cela sous-entend, me semble *franchement* incompatible avec l'idée que sous tend l'inlining...Comment inliner une fonction si on ne sait pas, à la base, quelle fonction appeler
    Pire, il peut tres bien, pour certains appels de la fonction, décider de l'inliner, et pas pour d'autres ... nous donnant une raison de plus de nous casser la tête

    Enfin, il faut quand même raison garder

    Depuis l'avénement du PIII et équivalent, les processeurs atteingent des fréquences d'horloges se comptant en... Ghz.

    Pour se représenter les chose, il faut imaginer que nous avons devant nous une "vieille horloge de grand mêre", l'une de celle avec le balancier apparent:

    Le nombre de Hertz correspond au nombres de battements que ce balancier effectue sur une seconde, c'est à dire, au nombre de fois où, en partant du centre, il:
    • part à l'extrème droite
    • repasse par le centre pour
    • partir à l'extrème gauche avant de
    • revenir au centre.

    Quand on commence à compter en gigahertz, comme c'est le cas pour les fréquences de processeurs, cela signifie qu'on se base sur une mesure sous forme de 1 000 000 000 battements par secondes, soit, 0,000 000 001 seconde par battement (évidemment, cela fait encore moins pour les processeurs tournant à 2Ghz )

    Comme cela reste *relativement* abstrait, voici quelques ordres de grandeur:
    • Un "arc réflex" (le réflex obtenu par le docteur quand il tappe juste en dessous de la rotule, et qui ne passe pas par le cerveau) se produit en... 0,017 seconde (+/-)
    • La fréquence à partir de laquelle on quitte le domaine du son pour passer à l'ultrason est de 20 000Hz
    • Si l'on savait obtenir une précision de l'ordre du gigahertz (donc de l'ordre de 0,000 000 001 seconde), pour calculer les distances interplanétaires,on obtiendrait une précision de l'ordre de 300 mètres (rappel: la vitesse de la lumière est de +/- 300 000 km/sec).
    • Il faut à peut près 0,15 à 0,2 seconde pour qu'un humain puisse réagir à un événement attendu en y apportant une réaction prévue (réfléchie avant d'être en situation de devoir apporter cette réaction).


    Ces choses étant fixées, il faut savoir que le fait d'appeler une fonction ne va utiliser que... quelques fréquences d'horloges (une très petite vingtaine, en comptant très large).

    Elle devrait donc être appelée plusieurs millions de fois (en dehors du temps qu'elle prend à s'exécuter) d'affilée avant que l'utilisateur ne commence à ressentir une différence entre l'exécution d'une fonction inline et celle d'une fonction appelée de manière classique.

    En ce qui concerne les fonctions virtuelles:

    Bien sûr, les fonctions virtuelles ont un surcoût par rapport aux fonctions "classiques"... Mais, là aussi, il faut raison garder:

    Une grande partie du surcoût apparait... au moment de la compilation et de l'édition de liens (quand il faut créer la vtable).

    Pour le reste, si différence il y a réellement entre l'appel d'une fonction classique et celui d'une fonction virtuelle, encore une fois, cela doit tourner aux alentours de quelques fréquences d'horloges au maximum...

    Une même cause ayant les même résultats, ce que j'ai écrit plus haut quant au gain d'une fonction "inlinée" par rapport à une fonction appelée de manière classique se retrouve exactement si on regarde une fonction appelée de manière classique et une fonction virtuelle

    Encore une fois, on en revient donc à cette fameuse phrase:
    Early optimisation is the root of all evil (une optimisation apparaissant trop tot est le chemin vers tous les maux)
    Je suis tout à fait d'accord que le gain ressenti sera bien plus important sur un antique 3x86 (ceux qui tournaient, au mieux, à 60Mhz), tout comme je suis tout à fait d'accord que ce n'est pas parce que les processeurs vont de plus en plus vite qu'il faut abandonner toute idée d'optimisation...

    Par contre, ce sur quoi je serai intraitable, c'est que, bien avant d'essayer de "grapiller" quelques fréquences d'horloges en évitant la virtualité d'une fonction ou en la déclarant inline, il faut commencer par vérifier les algorithmes de toutes les fonctions pour voir s'ils sont *vraiment* les plus efficaces dans la situation données, et, surtout, commencer par vérifier ceux qui présentent réellement un goulot d'étranglement.

    Mais pour cela, il n'y a rien à faire: il faut faire un audit en profondeur du code pour savoir précisément où ca coince

  4. #24
    Membre régulier
    Homme Profil pro
    Développeur .NET/C/C++
    Inscrit en
    Septembre 2007
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET/C/C++
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Septembre 2007
    Messages : 71
    Points : 122
    Points
    122
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Les fonctions "inline" pour commencer:

    Le fait de définir une fonction inline, de manière implicite ou explicite, ne fait que demander au compilateur de l'inliner... Mais il reste tout à fait libre de le faire... ou non
    Très juste.
    Citation Envoyé par koala01 Voir le message
    [*] l'idée même d'une fonction virtuelle, avec le problème de vtable que cela sous-entend, me semble *franchement* incompatible avec l'idée que sous tend l'inlining...Comment inliner une fonction si on ne sait pas, à la base, quelle fonction appeler
    C'est plus ou moins ce qui est expliqué dans l'article que j'ai donné en lien (avec quelques autres infos en plus) .

    Citation Envoyé par koala01 Voir le message
    En ce qui concerne les fonctions virtuelles:

    Bien sûr, les fonctions virtuelles ont un surcoût par rapport aux fonctions "classiques"... Mais, là aussi, il faut raison garder:

    Une grande partie du surcoût apparait... au moment de la compilation et de l'édition de liens (quand il faut créer la vtable).

    Pour le reste, si différence il y a réellement entre l'appel d'une fonction classique et celui d'une fonction virtuelle, encore une fois, cela doit tourner aux alentours de quelques fréquences d'horloges au maximum...
    En fait, j'avais dis ça au début parce qu'il parler d'accéder à un entier et que les performances semblent être un point important pour lui.
    Mais depuis, il a indiqué que cet entier n'était qu'un exemple et qu'en réalité il doit accéder à pas mal de variables (vecteurs et autres). Et il est clair que le cout d'un appel à une fonction virtuelle est largement inférieur au fait de devoir recopier toute ces données.

    Citation Envoyé par koala01 Voir le message
    Encore une fois, on en revient donc à cette fameuse phrase:
    Early optimisation is the root of all evil (une optimisation apparaissant trop tot est le chemin vers tous les maux)
    Je suis tout à fait d'accord que le gain ressenti sera bien plus important sur un antique 3x86 (ceux qui tournaient, au mieux, à 60Mhz), tout comme je suis tout à fait d'accord que ce n'est pas parce que les processeurs vont de plus en plus vite qu'il faut abandonner toute idée d'optimisation...

    Par contre, ce sur quoi je serai intraitable, c'est que, bien avant d'essayer de "grapiller" quelques fréquences d'horloges en évitant la virtualité d'une fonction ou en la déclarant inline, il faut commencer par vérifier les algorithmes de toutes les fonctions pour voir s'ils sont *vraiment* les plus efficaces dans la situation données, et, surtout, commencer par vérifier ceux qui présentent réellement un goulot d'étranglement.

    Mais pour cela, il n'y a rien à faire: il faut faire un audit en profondeur du code pour savoir précisément où ca coince
    Je suis d'accord. Le mieux est de commencer en utilisant les fonctions virtuelles. si jamais il y a des problèmes de performances, il sera toujours possible de rectifier le tir plus tard.

  5. #25
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Mais la leçon globale que Michel_57 devrait tirer de tout cela c'est:

    Commence par concevoir *correctement* ton système, réfléchis à des algorithmes le plus clairs, précis et "simples" possible, implémente cela de manière tout à fait normale...

    Une fois que le système fonctionnera seulement il sera temps de voir si les performances sont au rendez-vous.

    Si ce n'est pas le cas, commence par reprendre tous tes algorithmes, pour vérifier qu'ils sont bel et bien "les plus adaptés" à la situation donnée.

    Une fois que tous les algorithmes ont été vérifiés, s'il n'y a pas d'amélioration, cherche à repérer les fonctions/portions de code qui présentent réellement un goulot d'étranglement.

    Cependant, on dit souvent que 5% du code prend 90% du temps, donc, il s'agira de s'accrocher pour repérer le réel goulot d'étranglement

    Mais, une fois clairement identifié, il s'agira, de vérifier une nouvelle fois l'algorithme avant de - en dernier recours - réfléchir à une solution qui s'écarte des habitudes de codage classiques.

  6. #26
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    A propos du dynamic-cast (P vers A)
    Il n'y a pas de mystère, ca doit passer par un identifiant...

    Ou bien cet identifiant est rajouté automatiquement par le compilateur (RunTime Type Information), et un simple dynamic_cast<A> suffira.
    Ou bien cet identifiant est rajouté par le programmeur et il y a alors plusieurs méthodes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class P
    {
         virtual int  getTypeId() const = 0;
    };
     
    class A : public P
    {
         static const int s_typeId = 1;
         virtual int  getTypeId() const { return s_typeId; }
    };
    ou (si on veut que le getTypeId() soit rapide):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    class P
    {
         int m_typeId;
         protected:
            inline P(int t) : m_typeId(t) {}
     
         public:
            inline int getTypeId() const { return m_typeId; }
    };
     
    class A
    {
        public:
           static const int s_typeId = 1;
           inline A() : P(s_typeId ) {}
    };
    Duplication des P
    Maintenant vouloir dupliquer un P avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    P* newP = (P*)malloc(sizeof(oldP));
    memcpy(newP,oldP,sizeof(oldP));
    Est une ineptie... et extrèmement dangereux, surtout si P contient des pointeurs. Les objets pointés seront alors partagés entre les deux instances, et *personne* ne connaitra qui est le vrai propriétaire de l'objet pointé.
    Sans compter, comme déjà évoqué, que le code de construction ne sera pas appelé, alors que le destructeur lui le sera (probablement) !

    Une solution serait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    class P
    {
         public:
            inline P*  duplicate() const
            {
                 P* ret = _duplicate();
                 assert(ret->getTypeId() == getTypeId());
            }
         protected:
            P(const P& copyFrom) {}
            virtual P*   _duplicate() const = 0;
    };
     
    class A
    {
        public:
            A(const A& copyFrom) : P(copyFrom)
            {} 
     
        protected:
            virtual P* _duplicate() const 
            {
                return new A(*this);
            }
    }
    Mais ca ne marche pas !
    La raison est la gestion mémoire en C++ qui est relativement libre.... Prenons l'exemple d'utilisation suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    void* operator new (size_t s)
    {
        return malloc_dans_une_pile_speciale(s);
    }
    void operator delete (void* ptr)
    {
         free_dans_une_pile_speciale(ptr);
    }
     
    void main()
    {
         P*  instance1 = new A(); // <= allouée en utilisant le new 'local'
         P* instance2 = instance1->duplicate(); // <= allouée en utilisant le new 'classique'
         ...
         delete instance1;  // <= pas de bleme, désalloué en utilisant le delete 'local'
         delete instance2;  // <= PROBLEME ! désalloué en utilsant le delete 'local' !!!
    }
    Il faut donc, pour éviter tout problême surcharger l'opérateur new (et delete) de P.

  7. #27
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Pour ce qui est du dynamic_cast, on peut l'éviter en implementant une sorte de pattern visiteur :

    On a une classe mère : cConvertible
    Toutes les classes convertibles dérivent de cConvertible

    Par exemple, on a trois classe :

    cCube
    cSphere
    cPyramide

    La classe cConvertible est définie ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class cConvertible
    {
         cConvertible();
         ~cConvertible
     
         virtual cCube*      convertitEnCube() {return NULL;};
         virtual cSphere*    convertitEnSphere() {return NULL;};
         virtual cPyramide*  convertitEnPyramide() {return NULL;};
    }
    Et les autres classes surchargent les méthodes de conversions qui sont disponibles:

    par exemple (un cube est convertible en cube) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    cCube* cCube::convertitEnCube()
    {
        return this;
    }
     
    (Par contre, on ne surcharge pas les autres conversions, qui doivent renvoyer NULL car non valides)

    Ainsi, si on n'implemente pas explicitement une conversion, la fonction renvoit un pointeur NULL

    Le dynamic_cast s'ecrit donc maintenant comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    *pCube = pObjet->convertitEnCube();
    Si pObjet est convertible en cube, on obtient un pointeur de type cube
    Sinon, on obtient NULL

    Du coup :

    1/ c'est plus propre que le dynamic_cast (enfin, je prefere) car c'est maitrisé par le programmeur
    2/ on peut implementer des conversion spéciales, par exemple la fonction convertirEnCube() executée sur une sphere peut renvoyer un cube du même volume que la sphere.

    Par contre :

    1/ On utilise des fonctions virtuelles, donc on utilise quand même la RTTI
    2/ Il faut que tous les objets convertible aient une class mère commune

  8. #28
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par buzzkaido Voir le message
    Du coup :

    1/ c'est plus propre que le dynamic_cast (enfin, je prefere) car c'est maitrisé par le programmeur
    Donc, tu n'utilises pas de fonctions virtuelles, mais des pointeurs de fonction, car c'est contrôlé par le développeur et non pas par le langage ?
    Pour moi, c'est le fait d'avoir besoin de connaitre le type exact qui est parfois signe de mauvais design. A partir du moment où on a besoin de le faire, je ne connais pas mieux que dynamic_cast pour le faire.

    Citation Envoyé par buzzkaido Voir le message
    2/ on peut implementer des conversion spéciales, par exemple la fonction convertirEnCube() executée sur une sphere peut renvoyer un cube du même volume que la sphere.
    Ca peut être intéressant, mais pour moi, on va là bien plus loin qu'une conversion, sans que ça soit forcément explicite. Je préfèrerais une fonction boundingBox, par exemple.


    Citation Envoyé par buzzkaido Voir le message
    Par contre :
    3/ Ce n'est pas très extensible, puisqu'ajouter un volume demande à modifier tous les autres.

  9. #29
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    3/ Ce n'est pas très extensible, puisqu'ajouter un volume demande à modifier tous les autres.
    C'est plus simple que ça.

    Ajouter un volume nécessite d'ajouter uniquement la fonction de conversion dans la classe mère.

    Les fonctions étant héritées, tous les volumes connaissent automatiquement cette nouvelle fonction, qui renvoit NULL par défaut.

    Les seuls volumes à modifier sont ceux qui peuvent être convertis vers le nouveau.

Discussions similaires

  1. [PHP 5.0] Héritage et transtypage PDOStatement
    Par im-souf dans le forum Langage
    Réponses: 2
    Dernier message: 02/12/2011, 13h37
  2. Problème d'héritage et de transtypage
    Par Higgins dans le forum C#
    Réponses: 12
    Dernier message: 07/01/2011, 18h08
  3. transtypage, héritage et surcharge!
    Par Syphys dans le forum Langage
    Réponses: 3
    Dernier message: 16/11/2009, 14h44
  4. [C++]closure + héritage + transtypage
    Par JEG dans le forum C++Builder
    Réponses: 11
    Dernier message: 30/01/2004, 14h26
  5. Héritage entre Forms
    Par BarBal dans le forum Composants VCL
    Réponses: 7
    Dernier message: 29/08/2002, 17h44

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo