2017-12-27 123 views
16
#include <fstream> 
#include <iostream> 
#include <map> 

int main(int argc, char** argv) { 
    try { 
     std::map<std::string, int> m{{"a", 1}, {"b", 2}}; 
     std::cout << m.at("c") << std::endl; 
    } catch (const std::exception& e) { 
     std::cerr << e.what() << std::endl; 
    } 

    return 0; 
} 

W języku C++ podczas pobierania nieistniejącego klucza mapy wyjątek wygląda na map::at: key not found. Nie podano informacji o kluczu .Dlaczego wyjątek C++ nie dostarcza szczegółów połączenia?

Ponadto, jeśli użytkownik uzyskuje dostęp do nieistniejącego pliku, komunikat wyjątku std::ios_base::failure wygląda na ios_base::clear: unspecified iostream_category error. Nazwa o nazwie, która spowodowała wyjątek, nie została podana. W związku z tym może zająć trochę czasu, aby dowiedzieć się, gdzie wyjątek jest, jeśli istnieje wiele zastosowań map.at() lub ifstream is w projekcie.

W przeciwieństwie do tego, Python może powiedzieć KeyError: 'c' lub FileNotFoundError: [Errno 2] No such file or directory: 'foo'.

Czy to tylko konwencja C++? Dziękuję Ci.

+20

Pamiętaj, że w C++, klucz z mapy nie może być wymienialny na sznurku. – nos

+0

C# FileNotFoundException nie wspomina również o nazwie pliku, którego nie można znaleźć –

+1

@nos: w duchu stdlib powinieneś chyba powiedzieć "nie ma op", ale nawet wtedy można by twierdzić, że powinien może zrobić to dla wszystkich typów, gdzie to możliwe. Imho głównym powodem jest solidność: co się stanie, jeśli podczas próby złożenia ładnego i dandysowego komunikatu o błędzie napotkasz inny wyjątek? – PlasmaHH

Odpowiedz

37

Problem to model obiektu C++, który różni się od języka Python. Aby się przeciwstawić, najpierw odpowiedzmy: co Python przechowuje w obiekcie wyjątku, aby wydrukować klucz? Jest to odniesienie, które utrzymuje ten obiekt przy życiu.

Nie można tego zrobić po prostu w C++.

  1. std::out_of_range Nie można zapisać wskaźnika lub odniesienia do klawisza, jak jest. Program obsługi wyjątku może znajdować się w odległym bloku. Oznacza to, że klucz najprawdopodobniej wykracza poza zakres, zanim zostanie wprowadzony przewodnik. Otrzymujemy niezdefiniowane zachowanie, jeśli klucz zostanie wskazany.

  2. std::out_of_range to konkretna klasa. Nie jest to szablon taki jak std::map. Nie może łatwo skopiować klucza do samego siebie. Istnieje wiele różnych typów Key i oczywiście nie można ich uwzględnić za wszystkie. Nawet jeśli to możliwe, a co, jeśli klucz jest niezwykle drogi do skopiowania? A może nawet w ogóle nie do kopiowania? Nawet jeśli nie jest to prawdą, a co jeśli klucz nie jest wymienny na ciąg lub można go wydrukować?

Powyższe punkty nie oznaczają, że jest to niemożliwe. std::map::at może rzucić podkategorię std::out_of_range, która usuwa typ i tak dalej. Ale mam nadzieję, że widzisz, że ma on niebagatelny narzut. C++ polega na tym, że nie płaci się za wydajność w przypadku funkcji, których nie potrzebujesz lub używasz. Sprawienie, by wszyscy ponosili koszty bezwarunkowe, nie jest zgodne z tym projektem.

3

Standard C++ określa, że ​​map::at(const key_type& k) uruchomi wyjątek std::out_of_range (jeśli nie ma w nim wartości); nic więcej ... ale nic mniej; konkretna implementacja map::at(), ale problem polegałby na tym, że typ klucza k musi być przekonwertowany na char *. więc istnieje kilka opcji:

  1. Nie pokazuj tej informacji
  2. std::map nie przechowuje typy bez (przynajmniej implicite) konwersja do char *: ale nie byłoby wymaganie stosuje się do typu key_store który jest nie niezbędnego minimum
  3. Aby zapewnić różne komunikaty w std::out_of_range wyjątkiem zależności od key_type: ale to rozwiązanie nie zawsze pokazują oczekiwaną informację

w innym punkcie widzenia, std::out_of_range dziedziczy po std::logic_error; Standard C++ rozróżnia dwa główne rodzaje wyjątków:

  • logic_error: są spowodowane błędami w wewnętrznej logiki programu . Teoretycznie można im zapobiec.
  • runtime_error: są spowodowane wydarzeniami wykraczającymi poza zakres programu. Nie można ich łatwo przewidzieć z góry.

W naszym przypadku możemy łatwo sprawdzić istnienie elementu (tak nasza sprawa rozwiązuje z tego sprawy)

0

z C++, możliwe jest tworzenie spersonalizowanych wyjątek dziedziczenie z std :: wyjątek z dodatkowe informacje, takie jak nazwa pliku. Oto przykład:

#include <sstream> 

class MyException : public std::exception { 
    public : 
    MyException (std::exception e, std::string filename = "") : std::exception (e), str_ (filename) {} 
    MyException (const MyException& e) : std::exception (e), str_ (e.str_) {} 
    const char* what() const throw() { 
    std::ostringstream oss; 
    oss << "exception occured in " << str_ << " : "; 
    oss << std::exception::what(); 
    return oss.str().c_str(); 
    } 
    std::string str_; 
}; 

int main(int argc, char** argv) { 
    try { 
     try { 
      std::map<std::string, int> m{{"a", 1}, {"b", 2}}; 
      std::cout << m.at("c") << std::endl; 
     } 
     catch (const std::exception& e) { 
      MyException f (e, "main"); 
      throw (f); 
     } 
    } 
    catch (const MyException& e) { 
     std::cerr << e.what() << std::endl; 
    } 

    return 0; 
} 

wyświetli:

exception occured in main : std::exception 
Powiązane problemy