Инструкция C ++ Try-Catch не перехватывает исключение - PullRequest
2 голосов
/ 03 мая 2019

Я программирую очень простую структуру модульных тестов на C ++, которая использует операторы try-catch для анализа переменных. В этом примере у меня есть класс с именем UnitTest с одной общедоступной и одной закрытой функцией-членом с именами scalars_are_equal() и is_equal() соответственно. Пользователь должен передать str в публичную функцию, которая содержит строку с именем теста. Пользователь также должен передать value1 и value2 в публичную функцию, которая содержит два скалярных значения для сравнения друг с другом. Публичная функция использует оператор try и передает данные в приватную функцию, где две переменные оцениваются, чтобы проверить, совпадают ли они. Если значения совпадают, он возвращается к вызывающей функции, где на экран выводится сообщение, информирующее пользователя о том, что тест пройден. Если значения не равны, то закрытая функция должна выдать исключение, назначенное строке msg, а открытая функция должна перехватить это исключение. Класс прилагается ниже. Функции написаны как шаблонные функции, поэтому пользователь может сравнивать целые, плавающие и двойные числа, даже если арифметика с плавающей запятой может означать, что две версии числа не совсем идентичны.

class UnitTest
{
public:
    template <class type1, class type2>
    void scalars_are_equal(std::string str, const type1 &value1, const type2 &value2);
private:
    template <class type1, class type2>
    void is_equal(const type1 &value1, const type2 &value2, std::string str);
};
// ================================================================
// ================================================================
// UnitTest PUBLIC member functions
template <class type1, class type2>
void UnitTest::scalars_are_equal(std::string str, const type1 &value1,
                             const type2 &value2)

{
    unsigned long remain;
    remain = 60 - str.length();
    try {
        is_equal(value1, value2, str);
        std::cout << str + std::string(remain, '.') +
        std::string("PASSED") << std::endl;
    }
    catch (const char* msg) {
        std::cout << msg << std::endl;
    }
}
// ----------------------------------------------------------------

template <class type1, class type2>
void UnitTest::is_equal(const type1 &value1, const type2 &value2,
                        std::string str)
{
    if (value1 != value2) {
        unsigned long remain;
        remain = 60 - str.length();
        std::string msg = str + std::string(remain, '.') + " FAILED";
        throw msg;
    }

}

В этом случае основная программа выглядит так:

#include <iostream>
#include <string>
#include <vector>
#include <array>

#include "unit_test.hpp"

int main(int argc, const char * argv[]) {
    UnitTest q;
{    // Test to see if code catches unequal scalars
    float a, b;
    a = 20.0;
    b = 30.0;
    std::string c ("Scalars_Unequal_Test");
    q.scalars_are_equal(c, a, b);
}

По причинам, которые я не понимаю, оператор catch в функции scalars_are_equal() не перехватывает функцию is_equal(). Сначала я подумал, что это может быть потому, что функция выдает std::string, но когда я изменил оператор catch с const char на std :: string, это не имело значения. Кто-нибудь знает, почему это не ловит исключение?

Ответы [ 2 ]

8 голосов
/ 03 мая 2019

Вы бросаете std :: string, а не char * в UnitTest :: is_equal ().

std::string msg = str + std::string(remain, '.') + " FAILED";
throw msg;

Итак, вы должны поймать строку:

catch (std::string& msg) {
    std::cout << msg << std::endl;
}

Важное примечание: не бросайте std :: string или аналогичные, но классы, полученные из std :: exception или вашего собственного базового класса исключения. Например:

 std::string msg = str + std::string(remain, '.') + " FAILED";
 throw std::runtime_error(msg);
 [...]
 catch (std::runtime_error& e) {
    std::cout << e.what() << std::endl;
 }
0 голосов
/ 03 мая 2019

Тип в вашем улове должен совпадать с типом, который вы бросаете (или родительским классом этого типа).

Ваш код сводится к

try
{
  throw std::string( "test" );
}
catch ( const char* msg )
{
}

Вы бросаете std::string, но ловите const char*, поэтому исключение не перехватывается. Вам нужно либо бросить "test" напрямую, не заключая его в std::string, либо изменить свой улов на std::string&.

Бросание любого типа указателя приводит к проблемам владения, кто отвечает за освобождение указателя? Например, в вашем коде, если вы «исправили» проблему, вызвав throw msg.c_str(), строка msg будет уничтожена до того, как будет достигнут ваш блок catch, а указатель char* будет недействительным, чтобы исправить то, что вам придется динамически выделите новый массив char и скопируйте в него msg, тогда блок catch должен будет delete массив, но не может знать, что ему нужно.

Выбрасывание произвольных типов разрешено языком, но рекомендуется извлекать все ваши типы бросков из std::exception, тогда это позволяет вам писать код, подобный следующему, и иметь возможность перехватывать все исключения, генерируемые вашим кодом.

try
{
  // lots of code including calling third party libraries
}
catch ( std::exception& ex )
{
  std::cout << "uncaught exception: " << ex.what() << "\n";
}

Типы исключений обычно перехватываются по ссылке, поскольку это позволяет избежать их копирования в контекст перехвата и избежать проблем с разделением на производные классы.

...