1

J'utilise l'héritage virtuel avec une sélection de classes en C++. Il plante actuellement sur la destruction. Il semble bien se compiler dans les compilateurs en ligne, mais quand je cours dans Visual Studio, il plante.Placement d'un nouveau plantage lorsqu'il est utilisé avec la hiérarchie d'héritage virtuel dans Visual C++

J'ai une classe de base virtuelle pure, héritée virtuellement par son implémentation. J'ai ensuite une troisième classe qui hérite de la mise en œuvre régulièrement. J'utilise un système interne pour créer et libérer de la mémoire. Sous le capot il utilise un placement neuf avec un malloc aligné. Il utilise ensuite gratuitement pour libérer la mémoire. J'ai créé cet exemple minimum. Ce n'est pas exactement ce que je suis en train de faire, mais j'ai l'impression d'avoir un problème similaire.

#include <iostream> 
#include <string> 

int main() 
{ 
    class Animal { 
     public: 
     Animal() { } 
     virtual ~Animal() { } 
     virtual void eat() { } 

    }; 

    class Mammal : public virtual Animal { 
     public: 
     virtual void breathe() { } 

    }; 

    class WingedAnimal : public virtual Animal { 
     public: 
     virtual void flap() { } 
    }; 

    // A bat is a winged mammal 
    class Bat : public Mammal, public WingedAnimal { 

    }; 

    Animal* bat = new(malloc(sizeof(Bat))) Bat; 
    bat->~Animal(); 
    free(bat); 
    printf("Done!"); 
} 

Comme je l'ai dit, cet exemple affichera "Terminé" dans le compilateur en ligne. Cependant, dans Visual Studio 2015, il semble se bloquer sur l'objet chauve-souris. Je suis assez nouveau pour l'héritage virtuel et le placement nouveau. Quelqu'un voit-il le problème?

+0

@Barry http://cpp.sh/2vd4t fournit exactement cela ... coller dans le studio visuel et vous avez le problème. Voici un en ligne que vous pouvez utiliser: http://webcompiler.cloudapp.net/. Comme je vois beaucoup cette réponse sur presque toutes mes questions. Peut-être pourriez-vous indiquer ce qui me manque pour pouvoir m'améliorer dans le futur? – marsh

+2

La publication de liens vers le code est déconseillée. Code postal dans la question elle-même. –

+3

@jeff Il utilise la nouvelle syntaxe placement, donc malloc alloue juste la mémoire brute; new l'initialise en tant qu'objet C++. Puis il appelle explicitement le destructeur et appelle gratuitement. C'est légal. Et les classes dérivées ont toujours un destructeur virtuel lorsqu'elles héritent d'une classe qui en a un. –

Répondre

3

malloc retourne une adresse mémoire fraîche, operator new place un Bat à cette adresse, et la conversion en Animal*ajuste l'adresse. Maintenant, la variable bat pointe quelque part dans le bloc malloc. free c'est impossible.

Bat* bat0 = new(malloc(sizeof(Bat))) Bat; 
Animal* bat = bat0; 
std::cout << bat0 << " " << bat << "\n"; 

gcc imprime deux adresses identiques, tandis que VC++ imprime deux différents. L'un ou l'autre comportement est parfaitement normal et autorisé par la norme, même s'il n'y a pas d'héritage multiple. La plupart des compilateurs n'adaptent pas l'adresse avec un seul héritage, mais il y a quelques exceptions.

Pour être du bon côté, ne comptez pas sur les deux adresses étant les mêmes.

Il est possible de récupérer l'adresse d'origine par coulée dynamique void*:

free(dynamic_cast<void*>(bat)); 

devrait être OK. Bien sûr, une fonction virtuelle est nécessaire pour que la distribution dynamique fonctionne, comme d'habitude.

Mise à jour: dynamic_cast<void*> récupère le pointeur initial, mais free se bloque toujours avec VC++. Je ne sais pas pourquoi.

La bonne méthode pour intégrer un gestionnaire de mémoire tiers dans votre programme C++ est de surcharger operator new et operator delete

void* ::operator new(size_t sz) { return my_managed_malloc(sz); } 
void ::operator delete (void* ptr) { return my_managed_free(ptr); } 

placer dans une fichier C++ dans votre programme (si vous avez des DLL, puis dans tous les DLL) et utilisez normalement C++, sans astuces mal définies. Pour plus d'informations, http://en.cppreference.com/w/cpp/memory/new/operator_new.

+0

Merci beaucoup, c'est logique. J'hérite d'une base de code qui utilise les fonctions d'usine un CreateAnimal() et un DestroyAnimal(). Y a-t-il un moyen de faire fonctionner le système existant? L'animal dans mon cas est une pure classe virtuelle et chaque implémentation est entièrement cachée à l'utilisateur, donc je ne peux pas avoir une fonction CreateBat(). Aurais-je besoin de maintenir une référence interne au bon pointeur malloc? Est-ce que j'ai raison de supposer qu'une distribution au bon type ne fonctionnerait pas non plus parce que j'ai toujours la mauvaise adresse? – marsh

+0

Pourquoi l'appel à delete ne fait pas revenir la valeur? Tout comme si j'avais utilisé un nouvel appel normal? – marsh

+0

@marsh voir mise à jour. –