J'ai d'abord un fichier singleton.cpp pour construire un objet singleton, et déclarer l'instance en utilisant attribute[((constructor)) Et un simple main .cpp Je construis main.cpp singleton.cpp ensemble: Alors pourquoi mon programme plante, que s'est-il passé ? Comment le réparer? J'utilise gcc sur ubuntu. Merci beaucoup. g++ singleton.cpp main.cpp -o main
./main
beforeFunction
Segmentation fault
#include<iostream>
using namespace std;
int main(){
return 0;
}
#include<iostream>
using namespace std;
class singleton{
public:
singleton(){cout<<"singleton ctor\n";}
};
__attribute__((constructor)) static void beforeFunction()
{
printf("beforeFunction\n");
singleton obj;
}
3 Réponses :
Alors pourquoi mon programme plante, que s'est-il passé?
Très probablement, la machine iostream
n'est pas encore initialisée au moment où les fonctions __attribute __ ((constructeur))
sont exécutées.
Comment y remédier?
Soit utilisez C I / O comme printf
, ce qui semble fonctionner dans votre cas; ou mieux, évitez d'utiliser complètement __attribute __ ((constructeur))
(ce n'est pas du C ni du C ++ standard, c'est-à-dire que votre programme n'est pas portable).
Notez qu'il n'est pas nécessaire d'utiliser __attribute __ ((constructor))
pour créer des singletons ou des objets globaux.
J'ai reproduit ceci avec g ++ (Debian 7.3.0-5)
, et aussi g ++ (GCC) 9.0.0 20180902 (expérimental)
.
C'est intéressant que cela échoue:
$ g++ main.cpp singleton2.cpp && ./a.out singleton ctor $ g++ singleton2.cpp main.cpp && ./a.out singleton ctor
mais cela fonctionne comme prévu:
// singleton2.cpp #include<iostream> using namespace std; class singleton{ public: singleton(){cout<<"singleton ctor\n";} }; static singleton obj;
Comme Acorn l'a correctement indiqué, le iostream Les machines
/ std :: cout
n'ont pas été correctement initialisées au moment où vous avez appelé le constructeur singleton. Cela se produit car un code spécial est émis dans main.o
(et uniquement main.o
) qui appelle std :: ios_base :: Init :: Init ()
. Et seulement parce que main.cpp
a des #include
superflus .
Comment y remédier?
La meilleure solution est de ne pas utiliser du tout __attribute __ ((constructor))
. Dans votre cas, il n'y a aucune raison de faire ce que vous faites. Faites ceci à la place:
$ g++ main.cpp singleton.cpp && ./a.out beforeFunction singleton ctor
Avec le code ci-dessus, l'un ou l'autre ordre de liaison fonctionne:
$ g++ singleton.cpp main.cpp && ./a.out beforeFunction Segmentation fault
Si vous insistez pour utiliser __ attribut __ ((constructeur)) , puis assurez-vous que main.o
est sur votre ligne de lien avant tout autre objet qui pourrait utiliser iostream s.
Je suis presque sûr que la commande main.cpp singleton.cpp
ne fonctionne que parce qu'il y a un #include
errant dans main.cpp
.
std :: cout
est initialisé à l'aide d'un objet C ++ statique, voir
:
// For construction of filebuffers for cout, cin, cerr, clog et. al. static ios_base::Init __ioinit;
Depuis votre le code repose à la fois sur ce constructeur statique et possède un constructeur ELF, il s'exécute dans ce limitation GCC :
Cependant, à l'heure actuelle, l'ordre dans lequel les constructeurs pour les objets C ++ avec une durée de stockage statique et les fonctions décorées avec l'attribut
constructeur
sont invoqués n'est pas spécifié.
Si vous utilisez un objet C ++ à la place, l'ordre est bien défini. Le manuel GCC suggère également d'utiliser le attribut init_priority
, mais comme vous ne pouvez pas l'appliquer à la définition de __ioinit
(sauf via le piratage du préprocesseur), je ne pense pas que cela soit utile dans ce contexte. p>