Question

Je continue à voir des gens dire que les exceptions sont lentes, mais je ne vois jamais aucune preuve. Ainsi, au lieu de demander si elles le sont, je vais demander comment les exceptions fonctionnent en arrière-plan, afin que je puisse prendre une décision concernant le moment de les utiliser et leur lenteur.

D'après ce que je sais, les exceptions sont la même chose que de faire beaucoup de retours, mais il vérifie également le moment où il doit cesser de faire le retour. Comment vérifie-t-il quand s'arrêter? Je suppose que je dis qu'il existe une deuxième pile qui contient le type d'exception et l'emplacement de la pile revient ensuite jusqu'à ce qu'il y parvienne. Je suppose également que le seul moment où la pile est touchée est un lancer et chaque essai / attrapé. AFAICT implémentant un comportement similaire avec un code retour prendrait le même temps. Mais tout cela n’est qu’une hypothèse, alors je veux savoir.

Comment fonctionnent réellement les exceptions?

Était-ce utile?

La solution

Au lieu de deviner, j'ai décidé de regarder le code généré avec un petit morceau de code C ++ et une installation un peu ancienne de Linux.

class MyException
{
public:
    MyException() { }
    ~MyException() { }
};

void my_throwing_function(bool throwit)
{
    if (throwit)
        throw MyException();
}

void another_function();
void log(unsigned count);

void my_catching_function()
{
    log(0);
    try
    {
        log(1);
        another_function();
        log(2);
    }
    catch (const MyException& e)
    {
        log(3);
    }
    log(4);
}

Je l'ai compilé avec g ++ -m32 -W -Wall -O3 -save-temps -c , puis j'ai examiné le fichier d'assemblage généré.

    .file   "foo.cpp"
    .section    .text._ZN11MyExceptionD1Ev,"axG",@progbits,_ZN11MyExceptionD1Ev,comdat
    .align 2
    .p2align 4,,15
    .weak   _ZN11MyExceptionD1Ev
    .type   _ZN11MyExceptionD1Ev, @function
_ZN11MyExceptionD1Ev:
.LFB7:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    popl    %ebp
    ret
.LFE7:
    .size   _ZN11MyExceptionD1Ev, .-_ZN11MyExceptionD1Ev

_ZN11MyExceptionD1Ev est MyException :: ~ MyException () , le compilateur a donc décidé qu'il fallait une copie non en ligne du destructeur.

.globl __gxx_personality_v0
.globl _Unwind_Resume
    .text
    .align 2
    .p2align 4,,15
.globl _Z20my_catching_functionv
    .type   _Z20my_catching_functionv, @function
_Z20my_catching_functionv:
.LFB9:
    pushl   %ebp
.LCFI2:
    movl    %esp, %ebp
.LCFI3:
    pushl   %ebx
.LCFI4:
    subl    $20, %esp
.LCFI5:
    movl    
    .text
    .align 2
    .p2align 4,,15
.globl _Z20my_throwing_functionb
    .type   _Z20my_throwing_functionb, @function
_Z20my_throwing_functionb:
.LFB8:
    pushl   %ebp
.LCFI6:
    movl    %esp, %ebp
.LCFI7:
    subl    $24, %esp
.LCFI8:
    cmpb    
    .weak   _ZTI11MyException
    .section    .rodata._ZTI11MyException,"aG",@progbits,_ZTI11MyException,comdat
    .align 4
    .type   _ZTI11MyException, @object
    .size   _ZTI11MyException, 8
_ZTI11MyException:
    .long   _ZTVN10__cxxabiv117__class_type_infoE+8
    .long   _ZTS11MyException
    .weak   _ZTS11MyException
    .section    .rodata._ZTS11MyException,"aG",@progbits,_ZTS11MyException,comdat
    .type   _ZTS11MyException, @object
    .size   _ZTS11MyException, 14
_ZTS11MyException:
    .string "11MyException"
, 8(%ebp) jne .L21 leave ret .L21: movl $1, (%esp) call __cxa_allocate_exception movl

Au lieu de deviner, j'ai décidé de regarder le code généré avec un petit morceau de code C ++ et une installation un peu ancienne de Linux.

class MyException
{
public:
    MyException() { }
    ~MyException() { }
};

void my_throwing_function(bool throwit)
{
    if (throwit)
        throw MyException();
}

void another_function();
void log(unsigned count);

void my_catching_function()
{
    log(0);
    try
    {
        log(1);
        another_function();
        log(2);
    }
    catch (const MyException& e)
    {
        log(3);
    }
    log(4);
}

Je l'ai compilé avec g ++ -m32 -W -Wall -O3 -save-temps -c , puis j'ai examiné le fichier d'assemblage généré.

    .file   "foo.cpp"
    .section    .text._ZN11MyExceptionD1Ev,"axG",@progbits,_ZN11MyExceptionD1Ev,comdat
    .align 2
    .p2align 4,,15
    .weak   _ZN11MyExceptionD1Ev
    .type   _ZN11MyExceptionD1Ev, @function
_ZN11MyExceptionD1Ev:
.LFB7:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    popl    %ebp
    ret
.LFE7:
    .size   _ZN11MyExceptionD1Ev, .-_ZN11MyExceptionD1Ev

_ZN11MyExceptionD1Ev est MyException :: ~ MyException () , le compilateur a donc décidé qu'il fallait une copie non en ligne du destructeur.

.globl __gxx_personality_v0
.globl _Unwind_Resume
    .text
    .align 2
    .p2align 4,,15
.globl _Z20my_catching_functionv
    .type   _Z20my_catching_functionv, @function
_Z20my_catching_functionv:
.LFB9:
    pushl   %ebp
.LCFI2:
    movl    %esp, %ebp
.LCFI3:
    pushl   %ebx
.LCFI4:
    subl    $20, %esp
.LCFI5:
    movl    
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0x0
    .byte   0x1
    .string "zPL"
    .uleb128 0x1
    .sleb128 -4
    .byte   0x8
    .uleb128 0x6
    .byte   0x0
    .long   __gxx_personality_v0
    .byte   0x0
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .byte   0x88
    .uleb128 0x1
    .align 4
.LECIE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .long   .LFB9
    .long   .LFE9-.LFB9
    .uleb128 0x4
    .long   .LLSDA9
    .byte   0x4
    .long   .LCFI2-.LFB9
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI3-.LCFI2
    .byte   0xd
    .uleb128 0x5
    .byte   0x4
    .long   .LCFI5-.LCFI3
    .byte   0x83
    .uleb128 0x3
    .align 4
.LEFDE3:
.LSFDE5:
    .long   .LEFDE5-.LASFDE5
.LASFDE5:
    .long   .LASFDE5-.Lframe1
    .long   .LFB8
    .long   .LFE8-.LFB8
    .uleb128 0x4
    .long   0x0
    .byte   0x4
    .long   .LCFI6-.LFB8
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI7-.LCFI6
    .byte   0xd
    .uleb128 0x5
    .align 4
.LEFDE5:
    .ident  "GCC: (GNU) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)"
    .section    .note.GNU-stack,"",@progbits
, (%esp) .LEHB0: call _Z3logj .LEHE0: movl $1, (%esp) .LEHB1: call _Z3logj call _Z16another_functionv movl $2, (%esp) call _Z3logj .LEHE1: .L5: movl $4, (%esp) .LEHB2: call _Z3logj addl $20, %esp popl %ebx popl %ebp ret .L12: subl $1, %edx movl %eax, %ebx je .L16 .L14: movl %ebx, (%esp) call _Unwind_Resume .LEHE2: .L16: .L6: movl %eax, (%esp) call __cxa_begin_catch movl $3, (%esp) .LEHB3: call _Z3logj .LEHE3: call __cxa_end_catch .p2align 4,,3 jmp .L5 .L11: .L8: movl %eax, %ebx .p2align 4,,6 call __cxa_end_catch .p2align 4,,6 jmp .L14 .LFE9: .size _Z20my_catching_functionv, .-_Z20my_catching_functionv .section .gcc_except_table,"a",@progbits .align 4 .LLSDA9: .byte 0xff .byte 0x0 .uleb128 .LLSDATT9-.LLSDATTD9 .LLSDATTD9: .byte 0x1 .uleb128 .LLSDACSE9-.LLSDACSB9 .LLSDACSB9: .uleb128 .LEHB0-.LFB9 .uleb128 .LEHE0-.LEHB0 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB1-.LFB9 .uleb128 .LEHE1-.LEHB1 .uleb128 .L12-.LFB9 .uleb128 0x1 .uleb128 .LEHB2-.LFB9 .uleb128 .LEHE2-.LEHB2 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB3-.LFB9 .uleb128 .LEHE3-.LEHB3 .uleb128 .L11-.LFB9 .uleb128 0x0 .LLSDACSE9: .byte 0x1 .byte 0x0 .align 4 .long _ZTI11MyException .LLSDATT9:

Surprise! Il n'y a aucune instruction supplémentaire sur le chemin du code normal. À la place, le compilateur a généré des blocs de code de correction hors ligne supplémentaires, référencés via une table à la fin de la fonction (qui est en réalité placé dans une section distincte de l'exécutable). Tout le travail est effectué dans les coulisses par la bibliothèque standard, basée sur ces tables ( _ZTI11MyException est typeinfo pour MyException ).

OK, ce n’était pas vraiment une surprise pour moi, je savais déjà comment ce compilateur le faisait. Poursuivant avec la sortie de l'assemblage:

<*>

Nous voyons ici le code permettant de lancer une exception. Bien qu'il n'y ait pas de frais généraux supplémentaires simplement parce qu'une exception peut être levée, il y a évidemment beaucoup de frais généraux lors du lancement et de la capture d'une exception. La plupart d’entre elles sont cachées dans __ cxa_throw , qui doit:

  • Parcourez la pile à l'aide des tables d'exceptions jusqu'à ce qu'elle trouve un gestionnaire pour cette exception.
  • Détendez la pile jusqu'à ce qu'elle parvienne à ce gestionnaire.
  • Appelez réellement le gestionnaire.

Comparez cela au coût de simplement renvoyer une valeur et vous voyez pourquoi les exceptions ne devraient être utilisées que pour des retours exceptionnels.

Pour terminer, le reste du fichier d'assemblage:

<*>

Les données typeinfo.

<*>

Encore plus de tables de traitement des exceptions et d’informations complémentaires.

Donc, conclusion, du moins pour GCC sous Linux: le coût est un espace supplémentaire (pour les gestionnaires et les tables), que des exceptions soient générées ou non, plus le coût supplémentaire lié à l'analyse des tables et à l'exécution des gestionnaires lorsqu'une exception est jeté. Si vous utilisez des exceptions au lieu de codes d'erreur et qu'une erreur est rare, elle peut être plus rapide , car vous n'avez plus la surcharge de rechercher des erreurs.

Si vous souhaitez plus d'informations, en particulier ce que font toutes les fonctions __ cxa _ , consultez la spécification d'origine à l'origine:

ZN11MyExceptionD1Ev, 8(%esp) movl

Au lieu de deviner, j'ai décidé de regarder le code généré avec un petit morceau de code C ++ et une installation un peu ancienne de Linux.

class MyException
{
public:
    MyException() { }
    ~MyException() { }
};

void my_throwing_function(bool throwit)
{
    if (throwit)
        throw MyException();
}

void another_function();
void log(unsigned count);

void my_catching_function()
{
    log(0);
    try
    {
        log(1);
        another_function();
        log(2);
    }
    catch (const MyException& e)
    {
        log(3);
    }
    log(4);
}

Je l'ai compilé avec g ++ -m32 -W -Wall -O3 -save-temps -c , puis j'ai examiné le fichier d'assemblage généré.

    .file   "foo.cpp"
    .section    .text._ZN11MyExceptionD1Ev,"axG",@progbits,_ZN11MyExceptionD1Ev,comdat
    .align 2
    .p2align 4,,15
    .weak   _ZN11MyExceptionD1Ev
    .type   _ZN11MyExceptionD1Ev, @function
_ZN11MyExceptionD1Ev:
.LFB7:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    popl    %ebp
    ret
.LFE7:
    .size   _ZN11MyExceptionD1Ev, .-_ZN11MyExceptionD1Ev

_ZN11MyExceptionD1Ev est MyException :: ~ MyException () , le compilateur a donc décidé qu'il fallait une copie non en ligne du destructeur.

.globl __gxx_personality_v0
.globl _Unwind_Resume
    .text
    .align 2
    .p2align 4,,15
.globl _Z20my_catching_functionv
    .type   _Z20my_catching_functionv, @function
_Z20my_catching_functionv:
.LFB9:
    pushl   %ebp
.LCFI2:
    movl    %esp, %ebp
.LCFI3:
    pushl   %ebx
.LCFI4:
    subl    $20, %esp
.LCFI5:
    movl    <*>, (%esp)
.LEHB0:
    call    _Z3logj
.LEHE0:
    movl    $1, (%esp)
.LEHB1:
    call    _Z3logj
    call    _Z16another_functionv
    movl    $2, (%esp)
    call    _Z3logj
.LEHE1:
.L5:
    movl    $4, (%esp)
.LEHB2:
    call    _Z3logj
    addl    $20, %esp
    popl    %ebx
    popl    %ebp
    ret
.L12:
    subl    $1, %edx
    movl    %eax, %ebx
    je  .L16
.L14:
    movl    %ebx, (%esp)
    call    _Unwind_Resume
.LEHE2:
.L16:
.L6:
    movl    %eax, (%esp)
    call    __cxa_begin_catch
    movl    $3, (%esp)
.LEHB3:
    call    _Z3logj
.LEHE3:
    call    __cxa_end_catch
    .p2align 4,,3
    jmp .L5
.L11:
.L8:
    movl    %eax, %ebx
    .p2align 4,,6
    call    __cxa_end_catch
    .p2align 4,,6
    jmp .L14
.LFE9:
    .size   _Z20my_catching_functionv, .-_Z20my_catching_functionv
    .section    .gcc_except_table,"a",@progbits
    .align 4
.LLSDA9:
    .byte   0xff
    .byte   0x0
    .uleb128 .LLSDATT9-.LLSDATTD9
.LLSDATTD9:
    .byte   0x1
    .uleb128 .LLSDACSE9-.LLSDACSB9
.LLSDACSB9:
    .uleb128 .LEHB0-.LFB9
    .uleb128 .LEHE0-.LEHB0
    .uleb128 0x0
    .uleb128 0x0
    .uleb128 .LEHB1-.LFB9
    .uleb128 .LEHE1-.LEHB1
    .uleb128 .L12-.LFB9
    .uleb128 0x1
    .uleb128 .LEHB2-.LFB9
    .uleb128 .LEHE2-.LEHB2
    .uleb128 0x0
    .uleb128 0x0
    .uleb128 .LEHB3-.LFB9
    .uleb128 .LEHE3-.LEHB3
    .uleb128 .L11-.LFB9
    .uleb128 0x0
.LLSDACSE9:
    .byte   0x1
    .byte   0x0
    .align 4
    .long   _ZTI11MyException
.LLSDATT9:

Surprise! Il n'y a aucune instruction supplémentaire sur le chemin du code normal. À la place, le compilateur a généré des blocs de code de correction hors ligne supplémentaires, référencés via une table à la fin de la fonction (qui est en réalité placé dans une section distincte de l'exécutable). Tout le travail est effectué dans les coulisses par la bibliothèque standard, basée sur ces tables ( _ZTI11MyException est typeinfo pour MyException ).

OK, ce n’était pas vraiment une surprise pour moi, je savais déjà comment ce compilateur le faisait. Poursuivant avec la sortie de l'assemblage:

<*>

Nous voyons ici le code permettant de lancer une exception. Bien qu'il n'y ait pas de frais généraux supplémentaires simplement parce qu'une exception peut être levée, il y a évidemment beaucoup de frais généraux lors du lancement et de la capture d'une exception. La plupart d’entre elles sont cachées dans __ cxa_throw , qui doit:

  • Parcourez la pile à l'aide des tables d'exceptions jusqu'à ce qu'elle trouve un gestionnaire pour cette exception.
  • Détendez la pile jusqu'à ce qu'elle parvienne à ce gestionnaire.
  • Appelez réellement le gestionnaire.

Comparez cela au coût de simplement renvoyer une valeur et vous voyez pourquoi les exceptions ne devraient être utilisées que pour des retours exceptionnels.

Pour terminer, le reste du fichier d'assemblage:

<*>

Les données typeinfo.

<*>

Encore plus de tables de traitement des exceptions et d’informations complémentaires.

Donc, conclusion, du moins pour GCC sous Linux: le coût est un espace supplémentaire (pour les gestionnaires et les tables), que des exceptions soient générées ou non, plus le coût supplémentaire lié à l'analyse des tables et à l'exécution des gestionnaires lorsqu'une exception est jeté. Si vous utilisez des exceptions au lieu de codes d'erreur et qu'une erreur est rare, elle peut être plus rapide , car vous n'avez plus la surcharge de rechercher des erreurs.

Si vous souhaitez plus d'informations, en particulier ce que font toutes les fonctions __ cxa _ , consultez la spécification d'origine à l'origine:

ZTI11MyException, 4(%esp) movl %eax, (%esp) call __cxa_throw .LFE8: .size _Z20my_throwing_functionb, .-_Z20my_throwing_functionb
, (%esp) .LEHB0: call _Z3logj .LEHE0: movl $1, (%esp) .LEHB1: call _Z3logj call _Z16another_functionv movl $2, (%esp) call _Z3logj .LEHE1: .L5: movl $4, (%esp) .LEHB2: call _Z3logj addl $20, %esp popl %ebx popl %ebp ret .L12: subl $1, %edx movl %eax, %ebx je .L16 .L14: movl %ebx, (%esp) call _Unwind_Resume .LEHE2: .L16: .L6: movl %eax, (%esp) call __cxa_begin_catch movl $3, (%esp) .LEHB3: call _Z3logj .LEHE3: call __cxa_end_catch .p2align 4,,3 jmp .L5 .L11: .L8: movl %eax, %ebx .p2align 4,,6 call __cxa_end_catch .p2align 4,,6 jmp .L14 .LFE9: .size _Z20my_catching_functionv, .-_Z20my_catching_functionv .section .gcc_except_table,"a",@progbits .align 4 .LLSDA9: .byte 0xff .byte 0x0 .uleb128 .LLSDATT9-.LLSDATTD9 .LLSDATTD9: .byte 0x1 .uleb128 .LLSDACSE9-.LLSDACSB9 .LLSDACSB9: .uleb128 .LEHB0-.LFB9 .uleb128 .LEHE0-.LEHB0 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB1-.LFB9 .uleb128 .LEHE1-.LEHB1 .uleb128 .L12-.LFB9 .uleb128 0x1 .uleb128 .LEHB2-.LFB9 .uleb128 .LEHE2-.LEHB2 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB3-.LFB9 .uleb128 .LEHE3-.LEHB3 .uleb128 .L11-.LFB9 .uleb128 0x0 .LLSDACSE9: .byte 0x1 .byte 0x0 .align 4 .long _ZTI11MyException .LLSDATT9:

Surprise! Il n'y a aucune instruction supplémentaire sur le chemin du code normal. À la place, le compilateur a généré des blocs de code de correction hors ligne supplémentaires, référencés via une table à la fin de la fonction (qui est en réalité placé dans une section distincte de l'exécutable). Tout le travail est effectué dans les coulisses par la bibliothèque standard, basée sur ces tables ( _ZTI11MyException est typeinfo pour MyException ).

OK, ce n’était pas vraiment une surprise pour moi, je savais déjà comment ce compilateur le faisait. Poursuivant avec la sortie de l'assemblage:

<*>

Nous voyons ici le code permettant de lancer une exception. Bien qu'il n'y ait pas de frais généraux supplémentaires simplement parce qu'une exception peut être levée, il y a évidemment beaucoup de frais généraux lors du lancement et de la capture d'une exception. La plupart d’entre elles sont cachées dans __ cxa_throw , qui doit:

  • Parcourez la pile à l'aide des tables d'exceptions jusqu'à ce qu'elle trouve un gestionnaire pour cette exception.
  • Détendez la pile jusqu'à ce qu'elle parvienne à ce gestionnaire.
  • Appelez réellement le gestionnaire.

Comparez cela au coût de simplement renvoyer une valeur et vous voyez pourquoi les exceptions ne devraient être utilisées que pour des retours exceptionnels.

Pour terminer, le reste du fichier d'assemblage:

<*>

Les données typeinfo.

<*>

Encore plus de tables de traitement des exceptions et d’informations complémentaires.

Donc, conclusion, du moins pour GCC sous Linux: le coût est un espace supplémentaire (pour les gestionnaires et les tables), que des exceptions soient générées ou non, plus le coût supplémentaire lié à l'analyse des tables et à l'exécution des gestionnaires lorsqu'une exception est jeté. Si vous utilisez des exceptions au lieu de codes d'erreur et qu'une erreur est rare, elle peut être plus rapide , car vous n'avez plus la surcharge de rechercher des erreurs.

Si vous souhaitez plus d'informations, en particulier ce que font toutes les fonctions __ cxa _ , consultez la spécification d'origine à l'origine:

Autres conseils

Les exceptions étant lentes était vrai dans le passé.
Dans la plupart des compilateurs modernes, cela n’est plus vrai.

Remarque: le fait que nous ayons des exceptions ne signifie pas que nous n'utilisons pas non plus les codes d'erreur. Quand une erreur peut être gérée localement, utilisez des codes d'erreur. Lorsque les erreurs nécessitent plus de contexte pour les exceptions d’utilisation de la correction: je l’ai écrit beaucoup plus éloquemment ici: Quels sont les principes qui guident votre politique de gestion des exceptions?

Le coût du code de traitement des exceptions lorsqu'aucune exception n'est utilisée est pratiquement nul.

Quand une exception est levée, du travail est fait.
Mais vous devez comparer cela au coût de la restitution des codes d'erreur et de leur vérification jusqu'au point où l'erreur peut être traitée. Les deux prennent plus de temps pour écrire et maintenir.

De plus, il existe un marché pour les novices:
Bien que les objets Exception soient supposés être petits, certaines personnes y mettent beaucoup de choses. Ensuite, vous avez le coût de la copie de l'objet exception. La solution est double:

  • Ne mettez pas d'objets supplémentaires dans votre exception.
  • Capture par référence de const.

À mon avis, je parierais que le même code avec des exceptions est plus efficace ou au moins aussi comparable que le code sans les exceptions (mais dispose de tout le code supplémentaire pour vérifier les résultats d'erreur de fonction). N'oubliez pas que vous n'obtenez rien gratuitement. Le compilateur génère le code que vous auriez dû écrire pour vérifier les codes d'erreur (et le compilateur est généralement beaucoup plus efficace qu'un humain).

Vous pouvez implémenter des exceptions de différentes manières, mais elles reposent généralement sur une prise en charge sous-jacente du système d'exploitation. Sous Windows, il s’agit du mécanisme structuré de gestion des exceptions.

Il existe une discussion intéressante sur les détails du projet de code: Comment un compilateur C ++ implémente la gestion des exceptions

La surcharge des exceptions se produit parce que le compilateur doit générer du code pour savoir quels objets doivent être détruits dans chaque cadre de pile (ou plus précisément, portée) si une exception se propage en dehors de cette étendue. Si une fonction ne comporte pas de variables locales sur la pile nécessitant l’appel de destructeurs, elle ne doit pas bénéficier d’une gestion des exceptions de pénalité de performance.

L'utilisation d'un code de retour ne peut dérouler qu'un seul niveau de la pile à la fois, alors qu'un mécanisme de traitement des exceptions peut sauter beaucoup plus loin dans la pile en une seule opération s'il n'y a rien à faire dans les cadres de pile intermédiaires.

Matt Pietrek a écrit un excellent article sur le Gestion des exceptions structurées Win32 . Bien que cet article ait été écrit en 1997, il s’applique toujours aujourd’hui (mais bien sûr, s’applique uniquement à Windows).

Cet article examine le problème et constate que, dans la pratique, il existe une analyse coût en temps aux exceptions, bien que le coût soit assez faible si l'exception n'est pas levée. Bon article, recommandé.

Un de mes amis a écrit un peu comment Visual C ++ gère les exceptions il y a quelques années.

http://www.xyzw.de/c160.html

Toutes les bonnes réponses.

Pensez également à quel point il est plus facile de déboguer du code qui "vérifie si" en tant que portes au sommet des méthodes au lieu de permettre au code de générer des exceptions.

Ma devise est qu’il est facile d’écrire du code qui fonctionne. Le plus important est d'écrire le code pour la prochaine personne qui le verra. Dans certains cas, c'est vous dans 9 mois et vous ne voulez pas maudire votre nom!

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