3 votes

Cette utilisation de c_str avec exception est-elle un comportement indéfini ?

J'ai vu plusieurs bouts de code similaires qui ressemblaient à ceci :

struct MyExcept : std::exception {
    explicit MyExcept(const char* m) noexcept : message{m} {}

    const char* what() const noexcept override {
        return message;
    }

    const char* message;
};

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept{error.c_str()};
}

int main() {
    try {
        foo();
    } catch (const MyExcept& e) {
        // Is this okay?
        std::cout << e.message << std::endl;
    }
}

Dans la ligne qui suit le commentaire Is this okay? nous lisons la chaîne de caractères en style c qui a été allouée dans le fichier foo en utilisant std::string . Puisque la chaîne est détruite avec le déroulement de la pile, est-ce un comportement non défini ?


S'il s'agit en effet d'un comportement non défini, que se passe-t-il si nous remplaçons l'élément main avec celui-ci ?

int main() {
    foo();
}

Comme il n'y a pas de catch, le compilateur n'est pas obligé de dérouler la pile, et pourtant de sortir le résultat de what() dans la console et abandonner le programme. Alors, est-ce que c'est toujours un comportement non défini ?

5voto

user0042 Points 7066

Oui, c'est un comportement indéfini. Vous travaillez avec un pointeur qui pend.

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept{error.c_str()};
} // <<  error goes out of scope here and so does the pointer returned
  //     from c_str()

Comme il n'y a pas de catch, le compilateur n'est pas obligé de dérouler la pile, et pourtant de sortir le résultat de what() dans la console et abandonner le programme. Alors, est-ce que c'est toujours un comportement non défini ?

Puisque l'implémentation par défaut utilisera std::terminate et à son tour à son tour d'appel std::abort() il peut s'agir d'un comportement encore indéfini, car la plupart des implémentations de gestionnaires standard essaieront de déréférencer what() .

Vous pouvez cependant installer vos propres gestionnaires pour éviter cela.

2voto

Rakete1111 Points 10248

Votre premier extrait a un comportement indéfini. [exception.ctor]/1 :

Lorsque le contrôle passe du point où une exception est levée à un gestionnaire, les destructeurs sont invoqués par un processus, spécifié dans cette section, appelé déroulement de la pile.

Ici, le destructeur ou error est appelé, ce qui entraîne le c_str() pour devenir un pointeur suspendu. Lors de son déréférencement ultérieur, lorsque vous utilisez la fonction std::cout par exemple, est un comportement indéfini.

Votre deuxième extrait est parfait. Il n'y a aucune raison pour que ce soit un comportement non défini. Vous n'appelez jamais réellement what ou de faire quoi que ce soit d'autre qui pourrait entraîner un comportement non défini. La seule chose qui n'est pas définie par la norme est si le déroulement de la pile se produit ou non, [except.terminate]/2 :

Dans le cas où aucun gestionnaire correspondant n'est trouvé, le fait que la pile soit déroulée ou non avant que std​::​terminate() s'appelle.

1voto

Remy Lebeau Points 130112

Comme d'autres l'ont dit, le code est indéfini puisque le pointeur assigné à message est laissé en suspens.

std::runtime_error apporte déjà une solution à ce problème. Appelez son constructeur qui prend un std::string comme entrée, et ne pas remplacer what() du tout :

struct MyExcept : std::runtime_error {
    explicit MyExcept(const std::string & m) noexcept : std::runtime_error(m) {}
};

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept(error);
}

int main() {
    try {
        foo();
    }
    catch (const MyExcept& e) {
        std::cout << e.what() << std::endl;
    }
}

std::runtime_error dispose d'une std::string dont les données what() retourne par défaut, évitant ainsi le problème de l'oscillation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X