Nasıl bir C ++ istisnası atmak


260

İstisna işleme konusunda çok zayıf bir anlayışa sahibim (örneğin, atma, deneme, kendi amaçlarım için ifadeleri yakalama).

Örneğin, aşağıdaki gibi bir işlev tanımladım: int compare(int a, int b){...}

Fonksiyonun, a veya b negatif olduğunda bazı mesajlarla istisna atmasını istiyorum.

Fonksiyon tanımında buna nasıl yaklaşmalıyım?



37
@OliCharlesworth, bunun temel bilgilerle karıştırılmış birine atmak için biraz fazla olduğunu düşünmüyor musunuz?
Mark Ransom

6
Gereksiz istisnalardan kaçınmaya değer. Arayanızın negatif değerler iletmesini istemiyorsanız unsigned int, işlev imzanızdaki parametreler olarak belirterek daha belirgin hale getirin . Sonra yine okulda, sadece gerçekten istisnai şeyler için istisnalar atmalı ve yakalamalısınız.
AJG85

1
@ Mark: Aslında bir kişinin throw()fonksiyonlar üzerinde istisna özellikleri kullanması gerekip gerekmediği sorusunu yanlış anladım .
Oliver Charlesworth

Yanıtlar:


364

Basit:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

Standart Kütüphane , atabileceğiniz yerleşik istisna nesneleri içeren güzel bir koleksiyonla birlikte gelir . Her zaman değere göre atmanız ve referans olarak yakalamanız gerektiğini unutmayın:

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

Her denemeden sonra birden fazla catch () ifadeniz olabilir, böylece isterseniz farklı istisna türlerini ayrı ayrı işleyebilirsiniz.

İstisnaları da yeniden atabilirsiniz:

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

Ve türden bağımsız olarak istisnaları yakalamak için:

catch( ... ) { };

26
Ve her zaman const olarak istisnaları yakalamanız gerekir
Adrian Cornish

2
@TerryLiYifeng, özel istisnalar daha mantıklıysa, o zaman gidin. Yine de std :: exception 'dan türetmek ve arabirimi aynı tutmak isteyebilirsiniz.
nsanders

2
Yine + 1'ed ama bence const oldukça önemli - çünkü şimdi geçici bir nesne olduğu gerçeğini vurgular - bu yüzden değişiklik işe yaramaz.
Adrian Cornish

2
@AdrianCornish: Bu gerçekten geçici değil. Sabit olmayan yakalamalar yararlı olabilir .
GManNickG

26
Genellikle throw;( throw e;yakalanan nesnenin bir kopyasını atmak, muhtemelen türünü değiştirmek ) yerine, basit bir şekilde (orijinal nesneyi yeniden yazmak ve türünü korumak) yeniden düşünürdünüz .
Mike Seymour

17

Sadece throwgerekli tryolanları ekleyin ve hatayı işleyen arayanı engelleyin. Kural olarak std::exception, sadece türetilmiş şeyleri atmalısınız , bu yüzden <stdexcept>önce ekleyin .

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

Ayrıca, Boost.Exception'a bakın .


15

Bu soru oldukça eski olmasına ve zaten cevaplanmış olmasına rağmen, sadece C ++ 11'de uygun istisna işlemeyi nasıl yapacağınıza dair bir not eklemek istiyorum:

Kullan std::nested_exceptionvestd::throw_with_nested

Burada ve burada StackOverflow üzerinde, hata ayıklayıcı veya hantal bir günlüklemeye gerek kalmadan kodunuzdaki istisnalarınız hakkında nasıl geri iz alabileceğinizi, yalnızca iç içe geçmiş istisnaları yeniden düzenleyecek uygun bir istisna işleyicisi yazarak açıklayabilirsiniz.

Bunu herhangi bir türetilmiş istisna sınıfıyla yapabileceğiniz için, böyle bir geri izlemeye çok fazla bilgi ekleyebilirsiniz! Ayrıca , bir backtrace'ın şöyle görüneceği GitHub'daki MWE'mize de göz atabilirsiniz :

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

8

Belirli bir hata oluştuğunda atılacak bir mesaj tanımlayabilirsiniz:

throw std::invalid_argument( "received negative value" );

ya da şöyle tanımlayabilirsiniz:

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

Genellikle, böyle bir try ... catchblok olurdu :

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }

6

İstedim ADD durumunda, ek bir not burada açıklanan diğer cevaplara özel istisnalar .

Kendi özel kural dışı durumunuzu oluşturduğunuzda, std::exception"tüm olası" kural dışı durum türlerini yakaladığınızda, catchyan tümceleri her zaman yakalanabilecek "en türetilmiş" kural dışı durum türüyle başlatmalısınız . Örneğe bakın ( YAPILMAMASI için):

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException - what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

NOT:

0) Uygun düzen tam tersi olmalıdır, yani önce onu catch (const MyException& e)takip edersiniz catch (const std::exception& e).

Gördüğünüz gibi olduğu gibi program çalıştırdığınızda 1), birinci yakalamak fıkra sen did muhtemelen budur (yürütülecektir DEĞİL ) ilk etapta istedi.

2) İlk yakalama yan tümcesinde yakalanan tür tür olsa da, std::exception"uygun" sürümü what()çağrılır - çünkü başvuru tarafından yakalanır (en azından yakalanan bağımsız değişken std::exceptiontürünü değere göre değiştirin - ve eylemde "nesne dilimleme" fenomenleri).

3) "XXX kural dışı durumunun atılmış olmasından dolayı bazı kodların ..." kural dışı durum türüne SAYGI İLE önemli şeyler yapması durumunda, burada kodunuzun hatalı davranışı vardır.

4) Yakalanan nesneler "normal" bir nesne ise: class Base{};ve class Derived : public Base {}...

5) g++ 7.3.0Ubuntu 18.04.1'de belirtilen sorunu belirten bir uyarı verir:

'Void illustrateDerivedExceptionCatch ()' işlevinde: item12Linux.cpp: 48: 2: uyarı: 'MyException' türünün istisnası catch olarak yakalanacak (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: uyarı: 'std :: exception' catch için önceki işleyici tarafından (const exception & e) ^ ~~~~

Yine , bu cevabın sadece burada açıklanan diğer cevaplara EKLEMEK olduğunu söyleyeceğim (bu noktadan bahsetmeye değer olduğunu düşündüm, ancak bir yorumda tasvir edemedim).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.