Domanda

Continuo a vedere la gente dire che le eccezioni sono lente, ma non vedo mai alcuna prova. Quindi, invece di chiedere se lo sono, chiederò come funzionano le eccezioni dietro la scena, così posso prendere una decisione su quando usarle e se sono lente.

Da quello che so, le eccezioni sono la stessa cosa di fare un sacco di ritorno, ma controlla anche quando deve smettere di fare il ritorno. Come controlla quando fermarsi? Sto indovinando e dicendo che c'è un secondo stack che contiene il tipo di eccezione e la posizione dello stack quindi ritorna fino a quando non arriva. Sto anche immaginando che l'unica volta in cui lo stack è touch è un lancio e ogni tentativo / cattura. AFAICT che implementa un comportamento simile con il codice di ritorno richiederebbe lo stesso tempo. Ma questa è tutta una supposizione, quindi voglio saperlo.

Come funzionano davvero le eccezioni?

È stato utile?

Soluzione

Invece di indovinare, ho deciso di esaminare il codice generato con un piccolo pezzo di codice C ++ e un'installazione Linux un po 'vecchia.

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);
}

L'ho compilato con g ++ -m32 -W -Wall -O3 -save-temps -c e ho guardato il file assembly generato.

    .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 è MyException :: ~ MyException () , quindi il compilatore ha deciso che aveva bisogno di una copia non in linea del distruttore.

.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

Invece di indovinare, ho deciso di esaminare il codice generato con un piccolo pezzo di codice C ++ e un'installazione Linux un po 'vecchia.

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);
}

L'ho compilato con g ++ -m32 -W -Wall -O3 -save-temps -c e ho guardato il file assembly generato.

    .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 è MyException :: ~ MyException () , quindi il compilatore ha deciso che aveva bisogno di una copia non in linea del distruttore.

.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:

Sorpresa! Non ci sono istruzioni aggiuntive sul normale percorso del codice. Il compilatore ha invece generato blocchi di codice di correzione extra-line extra, a cui fa riferimento una tabella alla fine della funzione (che in realtà è inserita in una sezione separata dell'eseguibile). Tutto il lavoro è svolto dietro le quinte dalla libreria standard, basata su queste tabelle ( _ZTI11MyException è typeinfo per MyException ).

OK, non è stata una sorpresa per me, sapevo già come ha fatto questo compilatore. Continuando con l'output dell'assembly:

<*>

Qui vediamo il codice per generare un'eccezione. Sebbene non ci sia stato un sovraccarico extra semplicemente perché potrebbe essere generata un'eccezione, c'è ovviamente un sacco di overhead nel lanciare e catturare un'eccezione. La maggior parte è nascosta in __cxa_throw , che deve:

  • Cammina nello stack con l'aiuto delle tabelle delle eccezioni fino a quando non trova un gestore per tale eccezione.
  • Svolgi la pila fino a quando non arriva a quel gestore.
  • In realtà chiama il gestore.

Confrontalo con il costo della semplice restituzione di un valore e capisci perché le eccezioni dovrebbero essere utilizzate solo per rendimenti eccezionali.

Per finire, il resto del file assembly:

<*>

I dati typeinfo.

<*>

Ancora più tabelle di gestione delle eccezioni e informazioni extra assortite.

Quindi, la conclusione, almeno per GCC su Linux: il costo è lo spazio extra (per i gestori e le tabelle) indipendentemente dal fatto che vengano generate o meno eccezioni, oltre al costo aggiuntivo dell'analisi delle tabelle e dell'esecuzione dei gestori quando un'eccezione è gettato. Se si utilizzano eccezioni anziché codici di errore e un errore è raro, può essere più veloce , poiché non si ha più il sovraccarico del test degli errori.

Se desideri maggiori informazioni, in particolare cosa fanno tutte le funzioni __cxa_ , vedi le specifiche originali da cui provengono:

ZN11MyExceptionD1Ev, 8(%esp) movl

Invece di indovinare, ho deciso di esaminare il codice generato con un piccolo pezzo di codice C ++ e un'installazione Linux un po 'vecchia.

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);
}

L'ho compilato con g ++ -m32 -W -Wall -O3 -save-temps -c e ho guardato il file assembly generato.

    .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 è MyException :: ~ MyException () , quindi il compilatore ha deciso che aveva bisogno di una copia non in linea del distruttore.

.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:

Sorpresa! Non ci sono istruzioni aggiuntive sul normale percorso del codice. Il compilatore ha invece generato blocchi di codice di correzione extra-line extra, a cui fa riferimento una tabella alla fine della funzione (che in realtà è inserita in una sezione separata dell'eseguibile). Tutto il lavoro è svolto dietro le quinte dalla libreria standard, basata su queste tabelle ( _ZTI11MyException è typeinfo per MyException ).

OK, non è stata una sorpresa per me, sapevo già come ha fatto questo compilatore. Continuando con l'output dell'assembly:

<*>

Qui vediamo il codice per generare un'eccezione. Sebbene non ci sia stato un sovraccarico extra semplicemente perché potrebbe essere generata un'eccezione, c'è ovviamente un sacco di overhead nel lanciare e catturare un'eccezione. La maggior parte è nascosta in __cxa_throw , che deve:

  • Cammina nello stack con l'aiuto delle tabelle delle eccezioni fino a quando non trova un gestore per tale eccezione.
  • Svolgi la pila fino a quando non arriva a quel gestore.
  • In realtà chiama il gestore.

Confrontalo con il costo della semplice restituzione di un valore e capisci perché le eccezioni dovrebbero essere utilizzate solo per rendimenti eccezionali.

Per finire, il resto del file assembly:

<*>

I dati typeinfo.

<*>

Ancora più tabelle di gestione delle eccezioni e informazioni extra assortite.

Quindi, la conclusione, almeno per GCC su Linux: il costo è lo spazio extra (per i gestori e le tabelle) indipendentemente dal fatto che vengano generate o meno eccezioni, oltre al costo aggiuntivo dell'analisi delle tabelle e dell'esecuzione dei gestori quando un'eccezione è gettato. Se si utilizzano eccezioni anziché codici di errore e un errore è raro, può essere più veloce , poiché non si ha più il sovraccarico del test degli errori.

Se desideri maggiori informazioni, in particolare cosa fanno tutte le funzioni __cxa_ , vedi le specifiche originali da cui provengono:

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:

Sorpresa! Non ci sono istruzioni aggiuntive sul normale percorso del codice. Il compilatore ha invece generato blocchi di codice di correzione extra-line extra, a cui fa riferimento una tabella alla fine della funzione (che in realtà è inserita in una sezione separata dell'eseguibile). Tutto il lavoro è svolto dietro le quinte dalla libreria standard, basata su queste tabelle ( _ZTI11MyException è typeinfo per MyException ).

OK, non è stata una sorpresa per me, sapevo già come ha fatto questo compilatore. Continuando con l'output dell'assembly:

<*>

Qui vediamo il codice per generare un'eccezione. Sebbene non ci sia stato un sovraccarico extra semplicemente perché potrebbe essere generata un'eccezione, c'è ovviamente un sacco di overhead nel lanciare e catturare un'eccezione. La maggior parte è nascosta in __cxa_throw , che deve:

  • Cammina nello stack con l'aiuto delle tabelle delle eccezioni fino a quando non trova un gestore per tale eccezione.
  • Svolgi la pila fino a quando non arriva a quel gestore.
  • In realtà chiama il gestore.

Confrontalo con il costo della semplice restituzione di un valore e capisci perché le eccezioni dovrebbero essere utilizzate solo per rendimenti eccezionali.

Per finire, il resto del file assembly:

<*>

I dati typeinfo.

<*>

Ancora più tabelle di gestione delle eccezioni e informazioni extra assortite.

Quindi, la conclusione, almeno per GCC su Linux: il costo è lo spazio extra (per i gestori e le tabelle) indipendentemente dal fatto che vengano generate o meno eccezioni, oltre al costo aggiuntivo dell'analisi delle tabelle e dell'esecuzione dei gestori quando un'eccezione è gettato. Se si utilizzano eccezioni anziché codici di errore e un errore è raro, può essere più veloce , poiché non si ha più il sovraccarico del test degli errori.

Se desideri maggiori informazioni, in particolare cosa fanno tutte le funzioni __cxa_ , vedi le specifiche originali da cui provengono:

Altri suggerimenti

Le eccezioni sono lente era vero ai vecchi tempi.
Nella maggior parte dei compilatori moderni questo non è più vero.

Nota: solo perché abbiamo delle eccezioni non significa che non utilizziamo anche i codici di errore. Quando l'errore può essere gestito localmente, utilizzare i codici di errore. Quando gli errori richiedono più contesto per la correzione, utilizzare le eccezioni: l'ho scritto molto più eloquentemente qui: Quali sono i principi che guidano la tua politica di gestione delle eccezioni?

Il costo del codice di gestione delle eccezioni quando non vengono utilizzate eccezioni è praticamente zero.

Quando viene generata un'eccezione, viene svolto del lavoro.
Ma devi confrontarlo con il costo di restituzione dei codici di errore e controllandoli fino al punto in cui è possibile gestire l'errore. Entrambi richiedono più tempo per scrivere e mantenere.

Inoltre c'è un gotcha per i principianti:
Sebbene gli oggetti Exception debbano essere piccoli, alcune persone inseriscono molte cose al loro interno. Quindi hai il costo di copiare l'oggetto eccezione. La soluzione è duplice:

  • Non mettere cose extra nella tua eccezione.
  • Cattura per riferimento const.

A mio avviso, scommetterei che lo stesso codice con le eccezioni è più efficiente o almeno paragonabile al codice senza le eccezioni (ma ha tutto il codice aggiuntivo per controllare i risultati dell'errore di funzione). Ricorda che non stai ottenendo nulla gratuitamente il compilatore sta generando il codice che dovresti aver scritto in primo luogo per controllare i codici di errore (e di solito il compilatore è molto più efficiente di un essere umano).

Esistono diversi modi in cui è possibile implementare le eccezioni, ma in genere si baseranno su un supporto sottostante dal sistema operativo. Su Windows questo è il meccanismo strutturato di gestione delle eccezioni.

C'è una discreta discussione dei dettagli su Code Project: Come un compilatore C ++ implementa la gestione delle eccezioni

Il sovraccarico delle eccezioni si verifica perché il compilatore deve generare codice per tenere traccia di quali oggetti devono essere distrutti in ogni frame dello stack (o più precisamente nell'ambito) se un'eccezione si propaga al di fuori di tale ambito. Se una funzione non ha variabili locali nello stack che richiedono di chiamare i distruttori, non dovrebbe avere una gestione delle eccezioni rispetto alla penalità di prestazione.

L'uso di un codice di ritorno può svolgere solo un singolo livello dello stack alla volta, mentre un meccanismo di gestione delle eccezioni può saltare molto più indietro nello stack in un'unica operazione se non c'è nulla da fare nei frame intermedi dello stack.

Matt Pietrek ha scritto un eccellente articolo su Gestione delle eccezioni strutturata Win32 . Mentre questo articolo è stato originariamente scritto nel 1997, si applica ancora oggi (ma ovviamente si applica solo a Windows).

Questo articolo esamina il problema e fondamentalmente scopre che in pratica esiste una corsa- costo delle eccezioni, anche se il costo è piuttosto basso se l'eccezione non viene generata. Buon articolo, consigliato.

Un mio amico ha scritto un po 'come Visual C ++ gestisce le eccezioni alcuni anni fa.

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

Tutte le risposte valide.

Inoltre, pensa a quanto è più facile eseguire il debug del codice che fa "se verifica" come porte in cima ai metodi invece di consentire al codice di generare eccezioni.

Il mio motto è che è facile scrivere codice che funzioni. La cosa più importante è scrivere il codice per la prossima persona che lo guarda. In alcuni casi, sei tu tra 9 mesi e non vuoi maledire il tuo nome!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top