Question

Le code suivant illustre un problème étrange que j'ai dans un projet Turbo C++ Explorer.L'un des trois objets de pile dans D::D() n'est pas détruit après avoir été hors de portée.

Cela ne se produit que s'ils sont compilés en mode release, les auto_ptrs a_ et b_ sont de types différents et l'exception levée n'hérite pas de std :: exception.Cela semble fonctionner très bien dans VC++ 2005 et C++ Builder 2009.J'ai installé la mise à jour 2 de BDS2006, le correctif cumulatif et le correctif 12.

Est-ce mon code ou le compilateur ?Connaissez-vous un correctif ?Ne pas pouvoir utiliser auto_ptr de manière fiable dans un projet VCL serait très gênant.


#include <memory>
#include <stdexcept>
#include <iostream>

typedef std::exception my_error; // will work fine if replaced with line below
//class my_error : public std::exception {};

class A {};
class B {};

class C
{
public:
    C(int id) : id_(id) { std::cout << "C::C() " << id_ << std::endl; };
    ~C() { std::cout << "C::~C() " << id_ << std::endl; };
private:
    int id_;
};

class D
{
public:
    D()
    {
        C c1(1);
        C c2(2);
        C c3(3);

        throw my_error();
    };

private:
    std::auto_ptr<A> a_;
    std::auto_ptr<B> b_; // will work fine if replaced with line below
//  std::auto_ptr<A> b_;
//  std::auto_ptr<C> c_; // see expected output
};

#pragma argsused
int main(int argc, char* argv[])
{
    try
    {
        D d;
    }
    catch (...)
    {
        std::cout << "caught exception" << std::endl;
    }

    return 0;
}


Attendu:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception


A obtenu:

C::C() 1
C::C() 2
C::C() 3
C::~C() 2
C::~C() 1
caught exception


Obtenu (avec la ligne '// std::auto_ptr<C> c_;' non commenté) :

C::C() 1
C::C() 2
C::C() 3
C::~C() 1
caught exception


Modifier: Modifications suggérées

Modifier 2 :
Je viens de le tester avec C++ Builder 2007 (11.0.2902.10471), qui montre le même problème.La configuration de la version fonctionne dès que je coche la case "Informations de débogage" dans Projet -> Options -> Compilateur C++ -> Débogage.Cela me surprend que l'exécutable devient plus petit avec les "Informations de débogage" activées (jusqu'à 31,5 Ko au lieu de 39,5 Ko).

Modifier 3 :
Dans Turbo C++ Explorer (C++ Builder 2006) (10.0.2288.42451), la configuration de la version fonctionne si je décoche la case "Extension de fonction en ligne (-vi)" dans Projet -> Options -> Compilateur C++ -> Débogage.Remplacement de la première ligne (#include <memory>) avec le code suivant, cela fonctionne également.

#pragma option push -vi-
#include <memory>
#pragma option pop 
Était-ce utile?

La solution

Cela semble être un bogue du compilateur. Je viens d'exécuter le même exemple dans VS2008SP1 et j'ai obtenu le résultat attendu.

Autres conseils

Pour ce que ça vaut, GCC 3.4.6 fait la chose attendue:

$ g++ main.cpp

$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception

C'est un bogue du compilateur dans C ++ Builder 2006. C ++ Builder 2009 le corrige; c'est la sortie que je reçois pour BCC v6.1:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception

Si une exception est levée dans un constructeur d'objet, le destructeur ne s'exécutera pas.

Le compilateur n'a aucun moyen de savoir si le constructeur est suffisamment terminé pour que le destructeur s'exécute correctement.

Voir http://www.parashift.com/ c ++ - faq-lite / exceptions.html # faq-17.4

EDIT: En réponse au commentaire ci-dessous ... Dans ce cas, il est fort probable qu'un bogue du compilateur lie la règle "ne pas exécuter le destructeur" avec une destruction incorrecte des objets sur la pile.

Peut-être que le flux de cout n'est pas vidé? Pouvez-vous essayer avec cerr à la place? ou directement mettre un point d'arrêt dans le destructeur et vérifier si elles sont touchées?

Je viens de tester cela sur la ligne de commande gratuite bcc5.5.1 et C++ Builder 6 bcc5.64 et les deux fonctionnent comme prévu - ce qui est une surprise compte tenu de leur âge.Ensuite, j'ai essayé cela dans C++ Builder 2007, bcc5.93 et ​​le bug y est présent.En fait, l'exemple de code pourrait être simplifié en types primitifs et le bug serait toujours présent :

class D
{
public:
    D();

private:
    std::auto_ptr<int>      a_;
    std::auto_ptr<short>    b_;
    std::auto_ptr<char>     c_;
    std::auto_ptr<bool>     d_;
};

Cet exemple extrême finit par provoquer l'appel d'aucun des destructeurs correspondants pour la classe C !Si vous souhaitez diagnostiquer ce bug plus en détail, une astuce que vous pouvez effectuer consiste à insérer un point d'arrêt d'assembly dans votre cteur D::D() :

// Note that D::D() ctor can't be inlined if it contains assembly
// limitation of borland compilers unfortunately
D::D()
{
    __asm int 3;
    C c1(1);
    C c2(2);
    C c3(3);

    throw my_error();
}

Vous le laisseriez ensuite passer par le débogueur.Lorsque l'exécution atteint ce point d'arrêt spécifié, le programme s'arrêtera et le contrôle sera retransféré au débogueur.Vous pouvez ensuite parcourir l’assemblage en une seule étape pour voir où se situe le problème.

Ressemble à un bogue dans le code de déroulement de la pile de gestion des exceptions. Essayez de créer une classe simple E avec une instance de celle-ci dans le constructeur de D et voyez si elle est appelée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top