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

MFC Discussion :

Problème de violation d'accès dans un tableau d'objets d'une classe dérivée


Sujet :

MFC

  1. #1
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    165
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 165
    Points : 106
    Points
    106
    Par défaut Problème de violation d'accès dans un tableau d'objets d'une classe dérivée
    Bonjour
    J'ai deux classes CHeisenbergSpin et CHeisenbergSpinEx dérivant de CHeisenbergSpin et l'exemple de code 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
    int nStartSize = 3;
    double dblPI = CTools::PI;
    CHeisenbergSpin **pSpin;
    pSpin = (CHeisenbergSpin**)new CHeisenbergSpinEx*[nStartSize];
    for (int nIndex = 0; nIndex < nStartSize; nIndex++)
    {
    	pSpin[nIndex] = new CHeisenbergSpinEx[nStartSize];
    	for (int i = 0; i < nStartSize; i++)
    		pSpin[nIndex][i].z = 1;
    }
    pSpin[0][0].Compute(dblPI / 3, dblPI);
    pSpin[0][1].Compute(dblPI / 2, dblPI / 3);
    pSpin[2][0].Compute(dblPI / 2, dblPI / 3);
    pSpin[2][1].Norm();
    pSpin[2][2].Compute(dblPI / 2, dblPI / 3);
    CHeisenbergSpinEx spinEx = *((CHeisenbergSpinEx*)&pSpin[2][2]);
    spinEx.Compute(dblPI / 2, dblPI/3);
    Dès que le 2ème indice du tableau est non nul ça génère une exception.
    j'ai remarqué que cela se produit lorsqu'on que la méthode appelée est surcharchée (Compute est surchargée par la classe dérivée)
    bien évidemment la ligne 17 fonctionne sans problème.

    J'ai également testé le code suivant

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    CHeisenbergSpin *pSpin = new CHeisenbergSpinEx[nStartSize];
    pSpin[0].Compute(dblPI / 2, dblPI / 3);
    pSpin[1].Compute(dblPI / 3, dblPI);
    pSpin[2].Compute(dblPI, dblPI);
    Les lignes 3 et 4 génèrent une exception.

    Pourquoi est ce que les objets à ces positions génèrent une excption
    Est ce qu'il y a un moyen d'appeller les fonctions surchargées sans faire un cast explicite

    J'espère que j'ai été assez clair
    Merci d'avance pour toute aide

  2. #2
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Tu adresses un buffer d'éléments de la classe dérivée comme un tableau d'éléments de la classe de base. Ça ne peut pas fonctionner : (CHeisenbergSpin *)p + i != (CHeisenbergSpinEx *)p + i (et même si sizeof(CHeisenbergSpin) == sizeof(CHeisenbergSpinEx), c'est un comportement indéterminé). Pour exploiter le polymorphisme sur les éléments de ta collection, il te faut une indirection supplémentaire (vecteur de pointeurs d'objets, par exemple).

  3. #3
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    165
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 165
    Points : 106
    Points
    106
    Par défaut
    Effectivement les deux classes n'ont pas le même sizeof. mais je fais un cast explicite chaque fois que je dois accéder à un élément propre à la classe dérivée.
    Matt qu'elle est la syntaxe pour créer un tableau à 2D ou 3D de pointeurs d'objets

  4. #4
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Ce n'est pas sorcier, par exemple :

    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
    27
    #include <cassert>
    #include <memory>
    #include <vector>
     
    template <typename BaseType>
    struct grid3d {
        typedef std::vector<std::unique_ptr<BaseType>> vector_t;
        typedef typename vector_t::size_type uscalar_t;
     
        grid3d()
         : grid3d(uscalar_t{8}, uscalar_t{8}, uscalar_t{8})
        { }
     
        grid3d(uscalar_t w_, uscalar_t h_, uscalar_t d_)
         : data(w_ * h_ * d_), w(w_), h(h_), d(d_)
        { }
     
        BaseType *at(uscalar_t x, uscalar_t y, uscalar_t z) const {
            assert(x < w && "x out of bounds");
            assert(y < h && "y out of bounds");
            assert(z < d && "z out of bounds");
            return data[z * (w * h) + y * w + x].get();
        }
     
        vector_t data;
        uscalar_t w, h, d;
    };

    Si tu veux absolument allouer ta grille en masse et bénéficier du polymorphisme sans revoir ta hiérachie de classes, tu peux aussi mettre ce genre de wrapper dans un tableau :

    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
    27
    28
    29
    30
    31
    32
    struct AnyOf {
    public:
        AnyOf()              : data(nullptr)                             { }
        AnyOf(Base    && in) : dataAsBase    (in), data(&dataAsBase    ) { }
        AnyOf(Derived1&& in) : dataAsDerived1(in), data(&dataAsDerived1) { }
        AnyOf(Derived2&& in) : dataAsDerived2(in), data(&dataAsDerived2) { }
        AnyOf(Derived3&& in) : dataAsDerived3(in), data(&dataAsDerived3) { }
        AnyOf(Derived4&& in) : dataAsDerived4(in), data(&dataAsDerived4) { }
     
        Base *operator->() { return data; }
     
     
    private:
        union {
            Base dataAsBase;
            Derived1 dataAsDerived1;
            Derived2 dataAsDerived2;
            Derived3 dataAsDerived3;
            Derived4 dataAsDerived4;
        };
     
        Base *data;
    };
     
    // ...
     
    #include <utility>
     
    AnyOf a;
    Derived2 d;
    // ...
    a = AnyOf(std::move(d));
    Il existe probablement des manière plus orthodoxes de faire ça, je suis une brêle en C++.


    Soit dit en passant tes sujets n'ont pas grand chose à voir avec les MFC, tu devrais poster sur le forum C++ général.

  5. #5
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    165
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 165
    Points : 106
    Points
    106
    Par défaut
    Merci Matt pour tes réponses
    Comme tu dis je vais déplacer mes sujets dans le forum C++
    mais comme j'ai mentionné plus
    Dès que le 2ème indice du tableau est non nul ça génère une exception.
    n'est pas lié à un alignement des données ou bien à la table des fonctions virtuelles , une idée comme ça

  6. #6
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 642
    Points
    7 642
    Par défaut
    Bonjour,

    En écrivant cette ligne, il y a aberration pSpin[nIndex] = new CHeisenbergSpinEx[nStartSize]; on associe un tableau de CHeisenbergSpinEx comme un tableau de CHeisenbergSpin.
    A l'utilisation l'adresse du 2nd élément obtenue en faisant pSpin[0][1]. se place sur adresse_table + taille CHeisenbergSpin, hors l'élément suivant est en réalité en adresse_table + taille CHeisenbergSpinEx.
    La fonction reçoit alors un objet dont ni le type ni l'adresse ne sont bonnes, et le plantage est ce que l'on peut espérer de mieux.
    Si l'adresse était bonne, le mécanisme des virtuels pourrait faire son boulot de retrouver le type-dérivé et la fonction. Ici c'est un problème d'adresse farfellue.
    Le problème vient du cast pSpin = (CHeisenbergSpin**)new CHeisenbergSpinEx*[nStartSize]; à la façon C. En faisant un cast C++, l'incohérence est vue immédiatement car pSpin = static_cast<CHeisenbergSpin**>(new CHeisenbergSpinEx*[nStartSize]); ne devrait pas compiler.

  7. #7
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    165
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 165
    Points : 106
    Points
    106
    Par défaut
    En effet dalfab, tu as parfaitement raison
    Le problème est qu'a l'exécution je peux créer des objets CHeisenbergSpinEx ou CHeisenbergSpin selon le choix de l'utilisateur final.
    et je voudrais le même pointeur tableau pour les stocker.
    La déclaration d'un pointeur tableau pour chaque type d'objet reviendra à dupliquer les traitements avec tout ce que cela entraîne

  8. #8
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 642
    Points
    7 642
    Par défaut
    Si on crée un tableau de pointeur au lieu d'un tableau d'objets, on pourra utiliser l'héritage. Ça sert à ça!

  9. #9
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    165
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 165
    Points : 106
    Points
    106
    Par défaut
    Je me suis mal exprimé, je voulais parler d'un tableau dynamique pour stocker les objets.
    Merci pour toutes vos contributions.

  10. #10
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    De manière générale, en C++, on ne peut pas convertir implicitement un ClasseDeBase** en ClasseDerivee**.
    Mais on peut avoir un tableau de ClasseDeBase* dont les éléments pointent vers des objets de classes dérivées.

    Un peu de code illustratif :
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #include <memory>
    #include <vector>
     
    struct Fruit {};
    struct Pomme : Fruit {};
    struct Poire : Fruit {};
     
    int uneFonction()
    {
    	Pomme pomme;
    	Poire poire;
     
    	// On n'a pas le droit de convertir un Pomme** en Fruit**.
    	Pomme* ptrPomme = &pomme;
    //	Fruit** ppFruit = &ptrPomme; // Interdit, car cela permettrait d'exécuter la ligne suivante.
    //	*ppFruit = &poire;           // ptrPomme est de type Pomme* mais pointe vers un objet de type Poire !
     
    	// Tableau, alloué dans la pile, de pointeurs vers des objets alloués dans la pile.
    	Fruit* tab1[] = {&pomme, &poire};
     
    	// Tableau, alloué dans la mémoire dynamique, de pointeurs vers des objets alloués dans la mémoire dynamique.
    	Fruit** tab2 = new Fruit*[2];
    	tab2[0] = nullptr;
    	tab2[1] = nullptr;
    	try {
    		tab2[0] = new Pomme;
    		tab2[1] = new Poire;
    	} catch(...) {
    		delete tab2[1];
    		delete tab2[0];
    		delete[] tab2;
    		throw;
    	}
    	delete tab2[1];
    	delete tab2[0];
    	delete[] tab2;
     
    	// Pareil que tab2, mais mieux car pas besoin de try-catch.
    	std::vector<std::unique_ptr<Fruit>> tab3;
    	tab3.emplace_back(new Pomme);
    	tab3.emplace_back(new Poire);
     
    	return 0;
    }

Discussions similaires

  1. Réponses: 2
    Dernier message: 25/01/2012, 12h40
  2. [D7] Violation d'accès dans l'IDE
    Par paradise dans le forum Langage
    Réponses: 1
    Dernier message: 08/02/2006, 16h10
  3. Message de violation d'accès dans la dcc50.dll
    Par cchatelain dans le forum EDI
    Réponses: 17
    Dernier message: 29/11/2005, 18h28
  4. Violation d'accès dans l'EDI sur compo1 apres suppr de comp2
    Par RamDevTeam dans le forum Composants VCL
    Réponses: 2
    Dernier message: 31/05/2005, 15h02
  5. Problème de violation d'accès
    Par Oluha dans le forum Bases de données
    Réponses: 11
    Dernier message: 31/05/2005, 10h26

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