Variable constante statique C ++ et destruction
-
06-07-2019 - |
Question
J'ai rencontré un comportement étrange avec une simple classe C ++.
classA.h
class A
{
public:
A();
~A();
static const std::string CONST_STR;
};
classA.cpp
#include "classA.h"
#include <cassert>
const std::string A::CONST_STR("some text");
A::A()
{
assert(!CONST_STR.empty()); //OK
}
A::~A()
{
assert(!CONST_STR.empty()); //fails
}
main.cpp
#include <memory>
#include <classA.h>
std::auto_ptr<A> g_aStuff;
int main()
{
//do something ...
g_aStuff = std::auto_ptr<A>(new A());
//do something ...
return 0;
}
Je m'attendrais à des violations d'accès ou à quelque chose de similaire, mais je ne m'attendrais jamais à ce que le contenu de la chaîne de const statique puisse changer. Quelqu'un at-il ici une bonne explication de ce qui se passe dans ce code?
merci, Norbert
La solution
Je m'attendrais à des violations d'accès ou quelque chose de similaire, mais je ne m'attendrais jamais que le contenu de la constante statique la chaîne pourrait changer.
Comportement indéfini: il est indéfini. Si CONST_STR a été détruit, une exception matérielle ne vous est pas garantie si vous y accédez. Il se peut qu’il plante, mais son adresse peut finir par contenir des données qui ressemblent à une chaîne vide: son destructeur peut effacer les pointeurs ou autre chose.
Dans ce cas, vous dites que l'instance A est également stockée dans un pointeur intelligent global, qui est attribué dans main (). Ainsi, CONST_STR a été construit lors de son accès dans le constructeur A, mais est probablement détruit avant le pointeur intelligent. Nous aurions besoin de tout le programme pour le dire avec certitude.
[Edit: vous l'avez fait. Puisque CONST_STR et g_aStuff sont définis dans différentes unités de compilation, leur ordre de construction relatif n’est pas défini par la norme. Je suppose que CONST_STR est détruit en premier.]
Autres conseils
Modifier: Apparemment, le A ::
manquant était une faute de frappe dans le message d'origine du code.
Réponse originale:
Voulez-vous dire
const std::string A::CONST_STR("some text");
afin que CONST_STR fasse partie de la classe A
?
Sinon, vous le déclarez séparément et pas l'initialisation du membre statique de A
.
Vous créez 2 variables statiques dans deux unités de compilation différentes. Il n'y a aucun moyen de savoir dans quel ordre ils sont initialisés. Mais leurs destructeurs sont toujours appelés dans l’ordre inverse.
Dans votre cas, il semble que le scénario suivant ait eu lieu:
g_aStuff constructor
CONST_STR constructor
main funciton initializes g_aStuff
CONST_str destructor
g_aStuff descrutor
À ce stade, le résultat de CONST_STR.empty () n’est pas défini. Ce qui peut déclencher une affirmation.
Le
const std::string CONST_STR("some text");
défini dans classA.cpp n'est pas membre de A. Cette définition ressemblerait à ceci:
const std::string A::CONST_STR("some text");
La norme ne spécifie pas l'ordre d'initialisation des objets globaux / statiques dans différentes unités de traduction. Toutefois, il garantit que chaque objet de ce type sera initialisé avant l'exécution d'une fonction quelconque de cette unité de traduction.
Dans votre cas, il arrive que CONST_STR
soit initialisé après g_aStuff
et, l'ordre de destruction étant inversé par rapport à l'ordre de construction, il est détruit avant. Ainsi, l'accès à CONST_STR
à partir de le destructeur de
appelle un comportement indéfini - vous pouvez obtenir une violation d'accès ou vous ne pouvez pas.
CONST_STR
est cependant initialisé avant le constructeur de Un constructeur
est exécuté car ils se trouvent dans la même unité de traduction.
Cela peut arriver s'il existe une instance globale de A (ou un membre de classe statique de type A). Dans la mesure où l'ordre d'initialisation des globaux et de la statique n'est pas défini (unités de traduction croisée), il peut l'être.
En regardant votre code complet, vous vous fiez à l'ordre de destruction entre les unités de compilation (classA.cpp et main.cpp). Si vous créez g_aStuff en tant que local dans main, vos assertions doivent être acceptées.