Ошибки времени компиляции

In one of my prof slides on ploymorphism, I see this piece of code with a couple of comments: discountVariable = //will produce (DiscountSale)saleVariable;//run-time error

In one of my prof slides on ploymorphism, I see this piece of code with a couple of comments:

discountVariable =              //will produce
  (DiscountSale)saleVariable;//run-time error
discountVariable = saleVariable //will produce
                                //compiler error

As you can see, it says in the first casting statement that it’ll produce run-time error and in the other one it says it’ll produce compiler error.

What makes these errors? and how they differ from each other?

The Unfun Cat's user avatar

The Unfun Cat

28.9k29 gold badges110 silver badges150 bronze badges

asked Feb 27, 2012 at 20:31

AbdullahR's user avatar

3

A run time error will only occur when the code is actually running.
These are the most difficult — and lead to program crashes and bugs in your code which can be hard to track down.

An example might be trying to convert a string: «hello» into an integer:

string helloWorld = "hello";
int willThrowRuntimeError = Convert.ToInt32(helloWorld);

The compiler may not see this as a problem but when run an error will be thrown.

Compiler errors are due to inaccuracies in code, where the compiler throws an error to alert you to something which will not compile, and therefore cannot be run.

An example of a compiler error would be:

int = "this is not an int";

user's user avatar

user

5,3316 gold badges19 silver badges35 bronze badges

answered Feb 27, 2012 at 20:38

DIXONJWDD's user avatar

DIXONJWDDDIXONJWDD

1,25610 silver badges20 bronze badges

1

A runtime error happens during the running of the program. A compiler error happens when you try to compile the code.

If you are unable to compile your code, that is a compiler error.

If you compile and run your code, but then it fails during execution, that is runtime.

answered Feb 27, 2012 at 20:33

James Montagne's user avatar

James MontagneJames Montagne

76.8k14 gold badges108 silver badges129 bronze badges

2

Compile time errors refers to syntax and semantics. For example, if you do operations that involves different types. Ex: adding a string with an int, or dividing a string by a real. (read the last paragraph thou!!!)

Run Time errors are those that are detected when the program execute. For example, division by zero. The compiler can not know if the operation x/a-b will leads to division by zero until the execution.

This is a very broad explanation. There are many smart compilers, and, also, is possible to do internal casting among different types that leads to operations that make sense. It it possible to pre-compile code and see some run time errors even if the code is not executed.

Refer to this link too: Runtime vs Compile time

Community's user avatar

answered Feb 27, 2012 at 20:37

Compile time errors are errors of syntax and semantics.

Run time errors are errors of logic primarily. Due to something the programmer has overlooked, the program crashes e.g. division by 0, accessing a variable without initializing it first etc.

answered Jun 8, 2015 at 7:03

Hadi's user avatar

HadiHadi

5008 silver badges20 bronze badges

Compile Time error means that the Compiler knows that discountVariable = saleVariable must be end with a semi colon as belowdiscountVariable = saleVariable;so it will throw an error when you compile the code.

Run Time error means that the error will occur at run time, because even though you are casting saleVariable into discountVariable, the cast cannot take because they differ in type.

Kasun Siyambalapitiya's user avatar

answered Feb 27, 2012 at 20:33

CodeBlue's user avatar

CodeBlueCodeBlue

14.5k32 gold badges94 silver badges131 bronze badges

think you’ve already got the general desc of what’s the difference. Specifically in the code you have shown in the OP,

  • In second statement, compiler compares the types on LHS and RHS and finds no implicit cast possible so it gives the error.
  • first statement is seen by compiler as the same, but here programmer explicitly casts the type, which is as good as telling compiler that I know what I’m doing and of course the compiler trusts you and gives you no errors.

answered Feb 27, 2012 at 20:50

Kashyap's user avatar

KashyapKashyap

14.5k12 gold badges63 silver badges100 bronze badges

1

Its because the compiler doesn’t know the object type of «saleVariable» until that value has actually been set when the program is running.

Your are forcing whatever is in salesVariable into the type DiscountSale this is considered unsafe and cannot be evaluated until runtime.

answered Feb 27, 2012 at 20:33

bigamil's user avatar

bigamilbigamil

6574 silver badges12 bronze badges

2

Compilation/Compile time/Syntax/Semantic errors: Compilation or compile time errors are error occurred due to typing mistake, if we do not follow the proper syntax and semantics of any programming language then compile time errors are thrown by the compiler. They wont let your program to execute a single line until you remove all the syntax errors or until you debug the compile time errors.
Example: Missing a semicolon in C or mistyping int as Int.

Runtime errors: Runtime errors are the errors that are generated when the program is in running state. These types of errors will cause your program to behave unexpectedly or may even kill your program. They are often referred as Exceptions.
Example: Suppose you are reading a file that doesn’t exist, will result in a runtime error.

Read more about all programming errors here

answered May 25, 2015 at 5:37

Pankaj Prakash's user avatar

If you’d use Google, you’d get this:

Compile time error is any type of error that prevent a java program compile like a syntax error, a class not found, a bad file name for the defined class, a possible loss of precision when you are mixing different java data types and so on.

A runtime error means an error which happens, while the program is running. To deal with this kind of errors java define Exceptions. Exceptions are objects represents an abnormal condition in the flow of the program. It can be either checked or unchecked.

http://wiki.answers.com/Q/Difference_between_run_time_error_and_compile_time_error_in_java

Community's user avatar

answered Feb 27, 2012 at 20:32

2

Compiler errors are due to inaccuracies in code, where the compiler throws an error to alert you to something which will not compile, and therefore cannot be run.

Ex :- MethodOverloading

class OverloadingTest {
    void sum(int a, long b) {
        System.out.println("a method invoked");
    }

    void sum(long a, int b) {
        System.out.println("b method invoked");
    }

    public static void main(String args[]) {
        OverloadingTest obj = new OverloadingTest();
        obj.sum(200, 200);// now ambiguity
    }
}

Run Time errors are those that are detected when the program execute. For example, division by zero. The compiler can not know if the operation x/a-b will leads to division by zero until the execution

answered Apr 21, 2015 at 8:48

Nikhil Kumar's user avatar

Nikhil KumarNikhil Kumar

2,5183 gold badges19 silver badges24 bronze badges

In one of my prof slides on ploymorphism, I see this piece of code with a couple of comments:

discountVariable =              //will produce
  (DiscountSale)saleVariable;//run-time error
discountVariable = saleVariable //will produce
                                //compiler error

As you can see, it says in the first casting statement that it’ll produce run-time error and in the other one it says it’ll produce compiler error.

What makes these errors? and how they differ from each other?

The Unfun Cat's user avatar

The Unfun Cat

28.9k29 gold badges110 silver badges150 bronze badges

asked Feb 27, 2012 at 20:31

AbdullahR's user avatar

3

A run time error will only occur when the code is actually running.
These are the most difficult — and lead to program crashes and bugs in your code which can be hard to track down.

An example might be trying to convert a string: «hello» into an integer:

string helloWorld = "hello";
int willThrowRuntimeError = Convert.ToInt32(helloWorld);

The compiler may not see this as a problem but when run an error will be thrown.

Compiler errors are due to inaccuracies in code, where the compiler throws an error to alert you to something which will not compile, and therefore cannot be run.

An example of a compiler error would be:

int = "this is not an int";

user's user avatar

user

5,3316 gold badges19 silver badges35 bronze badges

answered Feb 27, 2012 at 20:38

DIXONJWDD's user avatar

DIXONJWDDDIXONJWDD

1,25610 silver badges20 bronze badges

1

A runtime error happens during the running of the program. A compiler error happens when you try to compile the code.

If you are unable to compile your code, that is a compiler error.

If you compile and run your code, but then it fails during execution, that is runtime.

answered Feb 27, 2012 at 20:33

James Montagne's user avatar

James MontagneJames Montagne

76.8k14 gold badges108 silver badges129 bronze badges

2

Compile time errors refers to syntax and semantics. For example, if you do operations that involves different types. Ex: adding a string with an int, or dividing a string by a real. (read the last paragraph thou!!!)

Run Time errors are those that are detected when the program execute. For example, division by zero. The compiler can not know if the operation x/a-b will leads to division by zero until the execution.

This is a very broad explanation. There are many smart compilers, and, also, is possible to do internal casting among different types that leads to operations that make sense. It it possible to pre-compile code and see some run time errors even if the code is not executed.

Refer to this link too: Runtime vs Compile time

Community's user avatar

answered Feb 27, 2012 at 20:37

Compile time errors are errors of syntax and semantics.

Run time errors are errors of logic primarily. Due to something the programmer has overlooked, the program crashes e.g. division by 0, accessing a variable without initializing it first etc.

answered Jun 8, 2015 at 7:03

Hadi's user avatar

HadiHadi

5008 silver badges20 bronze badges

Compile Time error means that the Compiler knows that discountVariable = saleVariable must be end with a semi colon as belowdiscountVariable = saleVariable;so it will throw an error when you compile the code.

Run Time error means that the error will occur at run time, because even though you are casting saleVariable into discountVariable, the cast cannot take because they differ in type.

Kasun Siyambalapitiya's user avatar

answered Feb 27, 2012 at 20:33

CodeBlue's user avatar

CodeBlueCodeBlue

14.5k32 gold badges94 silver badges131 bronze badges

think you’ve already got the general desc of what’s the difference. Specifically in the code you have shown in the OP,

  • In second statement, compiler compares the types on LHS and RHS and finds no implicit cast possible so it gives the error.
  • first statement is seen by compiler as the same, but here programmer explicitly casts the type, which is as good as telling compiler that I know what I’m doing and of course the compiler trusts you and gives you no errors.

answered Feb 27, 2012 at 20:50

Kashyap's user avatar

KashyapKashyap

14.5k12 gold badges63 silver badges100 bronze badges

1

Its because the compiler doesn’t know the object type of «saleVariable» until that value has actually been set when the program is running.

Your are forcing whatever is in salesVariable into the type DiscountSale this is considered unsafe and cannot be evaluated until runtime.

answered Feb 27, 2012 at 20:33

bigamil's user avatar

bigamilbigamil

6574 silver badges12 bronze badges

2

Compilation/Compile time/Syntax/Semantic errors: Compilation or compile time errors are error occurred due to typing mistake, if we do not follow the proper syntax and semantics of any programming language then compile time errors are thrown by the compiler. They wont let your program to execute a single line until you remove all the syntax errors or until you debug the compile time errors.
Example: Missing a semicolon in C or mistyping int as Int.

Runtime errors: Runtime errors are the errors that are generated when the program is in running state. These types of errors will cause your program to behave unexpectedly or may even kill your program. They are often referred as Exceptions.
Example: Suppose you are reading a file that doesn’t exist, will result in a runtime error.

Read more about all programming errors here

answered May 25, 2015 at 5:37

Pankaj Prakash's user avatar

If you’d use Google, you’d get this:

Compile time error is any type of error that prevent a java program compile like a syntax error, a class not found, a bad file name for the defined class, a possible loss of precision when you are mixing different java data types and so on.

A runtime error means an error which happens, while the program is running. To deal with this kind of errors java define Exceptions. Exceptions are objects represents an abnormal condition in the flow of the program. It can be either checked or unchecked.

http://wiki.answers.com/Q/Difference_between_run_time_error_and_compile_time_error_in_java

Community's user avatar

answered Feb 27, 2012 at 20:32

2

Compiler errors are due to inaccuracies in code, where the compiler throws an error to alert you to something which will not compile, and therefore cannot be run.

Ex :- MethodOverloading

class OverloadingTest {
    void sum(int a, long b) {
        System.out.println("a method invoked");
    }

    void sum(long a, int b) {
        System.out.println("b method invoked");
    }

    public static void main(String args[]) {
        OverloadingTest obj = new OverloadingTest();
        obj.sum(200, 200);// now ambiguity
    }
}

Run Time errors are those that are detected when the program execute. For example, division by zero. The compiler can not know if the operation x/a-b will leads to division by zero until the execution

answered Apr 21, 2015 at 8:48

Nikhil Kumar's user avatar

Nikhil KumarNikhil Kumar

2,5183 gold badges19 silver badges24 bronze badges

Обработка ошибок и проектирование компилятора

Перевод статьи Error Handling in Compiler Designopen in new window.

Задача по обработке ошибок (Error Handling) включает в себя: обнаружение ошибок, сообщения об ошибках пользователю, создание стратегии восстановления и реализации обработки ошибок. Кроме того система обработки ошибок должна работать быстро.

Типы источников ошибок

Источники ошибок делятся на два типа: ошибки времени выполнения (run-time error) и ошибки времени компиляции (compile-time error).

Ошибки времени выполнения возникают когда программа запущена. Обычно они связаны с неверными входными данными. Примеры таких ошибок: недостаток памяти, конфликт с другим приложением, логические ошибки. Логическая ошибка означает что запуск программы не приводит к ожидаемому результату. Логические ошибки лучше всего обрабатывать тщательным тестированием и отладкой программы.

Ошибки времени компиляции возникают во время компиляции, до запуска программы. Примеры таких ошибок: синтаксическая ошибка или отсутствие файла с кодом на который есть ссылка.

Типы ошибок времени компиляции

Ошибки компиляции разделяются на:

  • Лексические (Lexical): включают в себя опечатки идентификаторов, ключевых слов и операторов
  • Синтаксические (Syntactical): пропущенная точка с запятой или незакрытая скобка
  • Семантические (Semantical): несовместимое значение при присвоении или несовпадение типов между оператором и операндом
  • Логические (Logical): недостижимый код, бесконечный цикл

Парсер, обрабатывая текст, пытается как можно раньше обнаружить ошибку. В современных средах разработки синтаксические ошибки отображаются прямо в редакторе кода, предотвращая последующий неверный ввод. Обнажение ошибки происходит когда введённый префикс не совпадает с префиксами строк верными в выбранном языке программирования. Например префикс for(;) может привести к сообщению об ошибке, так как обычно внутри for должно быть две точки с запятой.

Восстановление после ошибок

Базовое требование к компилятору — прервать компиляцию и выдать сообщение при появлении ошибки. Кроме этого есть несколько методов восстановления после ошибки.

Panic mode recovery

Это самый простой способ восстановления после ошибок и он предотвращает бесконечные циклы в компиляторе при попытках исправить ошибку. Парсер отклоняет следующие за ошибкой символы до того как будет обнаружен специальный символ (например, разделитель команд, точка с запятой). Такой подход адекватен если низкая вероятность нескольких ошибок в одной конструкции.

Пример: рассмотрим выражение с ошибкой (1 + + 2) + 3. При обнаружении второго + пропускаются все символы до следующего числа.

Phase level recovery

Производится локальное изменение входного потока чтобы исправить ошибку.

Error productions

Разработчики компиляторов знают часто встречаемые ошибки. При появлении таких ошибок могут применяться расширения грамматики для их обработки. Например: написание 5x вместо 5*x.

Global correction

Производится как можно меньше изменений чтобы преобразовать код с ошибкой в корректный код. Эту стратегию дорого реализовывать.

Разница между ошибками времени компиляции и ошибками времени выполнения

29.12.2019Разница между, Язык программирования

Ошибки времени компиляции . Ошибки, возникающие при нарушении правил написания синтаксиса, называются ошибками времени компиляции. Эта ошибка компилятора указывает на то, что должно быть исправлено, прежде чем код может быть скомпилирован. Все эти ошибки обнаруживаются компилятором и, таким образом, известны как ошибки времени компиляции.
Наиболее частые ошибки времени компиляции:

  • Отсутствует скобка ( } )
  • Печать значения переменной без ее объявления
  • Отсутствует точка с запятой (терминатор)

Ниже приведен пример для демонстрации ошибки времени компиляции:

  
#include<stdio.h>

void main()

{

    int x = 10;

    int y = 15; 

    printf("%d", (x, y)) 

}

Ошибка:

error: expected ';' before '}' token

Run-Time Ошибка: Ошибки , которые возникают во время выполнения программы (время выполнения) после успешной компиляции называются ошибки времени выполнения. Одной из наиболее распространенных ошибок времени выполнения является деление на ноль, также известное как ошибка деления. Эти типы ошибок трудно найти, так как компилятор не указывает на строку, в которой происходит ошибка.

Для большего понимания запустите пример, приведенный ниже.

  
#include<stdio.h>

void main()

{

    int n = 9, div = 0;

    div = n/0;

    printf("resut = %d", div);

}

Ошибка:

warning: division by zero [-Wdiv-by-zero]
     div = n/0;

В приведенном примере есть деление на ноль ошибок. Это пример ошибки во время выполнения, то есть ошибки, возникающие при запуске программы.

Различия между временем компиляции и ошибкой во время выполнения:

Compile-Time Errors Runtime-Errors
These are the syntax errors which are detected by the compiler. These are the errors which are not detected by the compiler and produce wrong results.
They prevent the code from running as it detects some syntax errors. They prevent the code from complete execution.
It includes syntax errors such as missing of semicolon(;), misspelling of keywords and identifiers etc. It includes errors such as dividing a number by zero, finding square root of a negative number etc.

Рекомендуемые посты:

  • Разница между ОС с разделением времени и ОС реального времени
  • Как избежать ошибки компиляции при определении переменных
  • Разница между процессором и графическим процессором
  • Разница между CLI и GUI
  • Разница между PNG и GIF
  • В чем разница между MMU и MPU?
  • Разница между BFS и DFS
  • Разница между RPC и RMI
  • Разница между C и C #
  • Разница между JSP и ASP
  • Разница между светодиодом и ЖК
  • Разница между MP4 и MP3
  • Разница между LAN, MAN и WAN
  • Разница между 4G и 5G
  • Разница между LAN и WAN

Разница между ошибками времени компиляции и ошибками времени выполнения

0.00 (0%) 0 votes

В C++ различают ошибки времени компиляции и ошибки времени выполнения. Ошибки первого типа обнаруживает компилятор до запуска программы. К ним относятся, например, синтаксические ошибки в коде. Ошибки второго типа проявляются при запуске программы. Примеры ошибок времени выполнения: ввод некорректных данных, некорректная работа с памятью, недостаток места на диске и т. д. Часто такие ошибки могут привести к неопределённому поведению программы.

Некоторые ошибки времени выполнения можно обнаружить заранее с помощью проверок в коде. Например, такими могут быть ошибки, нарушающие инвариант класса в конструкторе. Обычно, если ошибка обнаружена, то дальнейшее выполение функции не имеет смысла, и нужно сообщить об ошибке в то место кода, откуда эта функция была вызвана. Для этого предназначен механизм исключений.

Коды возврата и исключения

Рассмотрим функцию, которая считывает со стандартного потока возраст и возвращает его вызывающей стороне. Добавим в функцию проверку корректности возраста: он должен находиться в диапазоне от 0 до 128 лет. Предположим, что повторный ввод возраста в случае ошибки не предусмотрен.

int ReadAge() {
    int age;
    std::cin >> age;
    if (age < 0 || age >= 128) {
        // Что вернуть в этом случае?
    }
    return age;
}

Что вернуть в случае некорректного возраста? Можно было бы, например, договориться, что в этом случае функция возвращает ноль. Но тогда похожая проверка должна быть и в месте вызова функции:

int main() {
    if (int age = ReadAge(); age == 0) {
        // Произошла ошибка
    } else {
        // Работаем с возрастом age
    }
}

Такая проверка неудобна. Более того, нет никакой гарантии, что в вызывающей функции программист вообще её напишет. Фактически мы тут выбрали некоторое значение функции (ноль), обозначающее ошибку. Это пример подхода к обработке ошибок через коды возврата. Другим примером такого подхода является хорошо знакомая нам функция main. Только она должна возвращать ноль при успешном завершении и что-либо ненулевое в случае ошибки.

Другим способом сообщить об обнаруженной ошибке являются исключения. С каждым сгенерированным исключением связан некоторый объект, который как-то описывает ошибку. Таким объектом может быть что угодно — даже целое число или строка. Но обычно для описания ошибки заводят специальный класс и генерируют объект этого класса:

#include <iostream>

struct WrongAgeException {
    int age;
};

int ReadAge() {
    int age;
    std::cin >> age;
    if (age < 0 || age >= 128) {
        throw WrongAgeException(age);
    }
    return age;
}

Здесь в случае ошибки оператор throw генерирует исключение, которое представлено временным объектом типа WrongAgeException. В этом объекте сохранён для контекста текущий неправильный возраст age. Функция досрочно завершает работу: у неё нет возможности обработать эту ошибку, и она должна сообщить о ней наружу. Поток управления возвращается в то место, откуда функция была вызвана. Там исключение может быть перехвачено и обработано.

Перехват исключения

Мы вызывали нашу функцию ReadAge из функции main. Обработать ошибку в месте вызова можно с помощью блока try/catch:

int main() {
    try {
        age = ReadAge();  // может сгенерировать исключение
        // Работаем с возрастом age
    } catch (const WrongAgeException& ex) {  // ловим объект исключения
        std::cerr << "Age is not correct: " << ex.age << "n";
        return 1;  // выходим из функции main с ненулевым кодом возврата
    }
    // ...
}

Мы знаем заранее, что функция ReadAge может сгенерировать исключение типа WrongAgeException. Поэтому мы оборачиваем вызов этой функции в блок try. Если происходит исключение, для него подбирается подходящий catch-обработчик. Таких обработчиков может быть несколько. Можно смотреть на них как на набор перегруженных функций от одного аргумента — объекта исключения. Выбирается первый подходящий по типу обработчик и выполняется его код. Если же ни один обработчик не подходит по типу, то исключение считается необработанным. В этом случае оно пробрасывается дальше по стеку — туда, откуда была вызвана текущая функция. А если обработчик не найдётся даже в функции main, то программа аварийно завершается.

Усложним немного наш пример, чтобы из функции ReadAge могли вылетать исключения разных типов. Сейчас мы проверяем только значение возраста, считая, что на вход поступило число. Но предположим, что поток ввода досрочно оборвался, или на входе была строка вместо числа. В таком случае конструкция std::cin >> age никак не изменит переменную age, а лишь возведёт специальный флаг ошибки в объекте std::cin. Наша переменная age останется непроинициализированной: в ней будет лежать неопределённый мусор. Можно было бы явно проверить этот флаг в объекте std::cin, но мы вместо этого включим режим генерации исключений при таких ошибках ввода:

int ReadAge() {
    std::cin.exceptions(std::istream::failbit);
    int age;
    std::cin >> age;
    if (age < 0 || age >= 128) {
        throw WrongAgeException(age);
    }
    return age;
}

Теперь ошибка чтения в операторе >> у потока ввода будет приводить к исключению типа std::istream::failure. Функция ReadAge его не обрабатывает. Поэтому такое исключение покинет пределы этой функции. Поймаем его в функции main:

int main() {
    try {
        age = ReadAge();  // может сгенерировать исключения разных типов
        // Работаем с возрастом age
    } catch (const WrongAgeException& ex) {
        std::cerr << "Age is not correct: " << ex.age << "n";
        return 1;
    } catch (const std::istream::failure& ex) {
        std::cerr << "Failed to read age: " << ex.what() << "n";
        return 1;
    } catch (...) {
        std::cerr << "Some other exceptionn";
        return 1;
    }
    // ...
}

При обработке мы воспользовались функцией ex.what у исключения типа std::istream::failure. Такие функции есть у всех исключений стандартной библиотеки: они возвращают текстовое описание ошибки.

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

Исключения стандартной библиотеки

Функции и классы стандартной библиотеки в некоторых ситуациях генерируют исключения особых типов. Все такие типы выстроены в иерархию наследования от базового класса std::exception. Иерархия классов позволяет писать обработчик catch сразу на группу ошибок, которые представлены базовым классом: std::logic_error, std::runtime_error и т. д.

Вот несколько примеров:

  1. Функция at у контейнеров std::array, std::vector и std::deque генерирует исключение std::out_of_range при некорректном индексе.

  2. Аналогично, функция at у std::map, std::unordered_map и у соответствующих мультиконтейнеров генерирует исключение std::out_of_range при отсутствующем ключе.

  3. Обращение к значению у пустого объекта std::optional приводит к исключению std::bad_optional_access.

  4. Потоки ввода-вывода могут генерировать исключение std::ios_base::failure.

Исключения в конструкторах

В главе 3.1 мы написали класс Time. Этот класс должен был соблюдать инвариант на значение часов, минут и секунд: они должны были быть корректными. Если на вход конструктору класса Time передавались некорректные значения, мы приводили их к корректным, используя деление с остатком.

Более правильным было бы сгенерировать в конструкторе исключение. Таким образом мы бы явно передали сообщение об ошибке во внешнюю функцию, которая пыталась создать объект.

class Time {
private:
    int hours, minutes, seconds;

public:
    // Заведём класс для исключения и поместим его внутрь класса Time как в пространство имён
    class IncorrectTimeException {
    };

    Time::Time(int h, int m, int s) {
        if (s < 0 || s > 59 || m < 0 || m > 59 || h < 0 || h > 23) {
            throw IncorrectTimeException();
        }
        hours = h;
        minutes = m;
        seconds = s;
    }

    // ...
};

Генерировать исключения в конструкторах — совершенно нормальная практика. Однако не следует допускать, чтобы исключения покидали пределы деструкторов. Чтобы понять причины, посмотрим подробнее, что происходит при генерации исключения.

Свёртка стека

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

#include <exception>
#include <iostream>

void f() {
    std::cout << "Welcome to f()!n";
    Logger x;
    // ...
    throw std::exception();  // в какой-то момент происходит исключение
}

int main() {
    try {
        Logger y;
        f();
    } catch (const std::exception&) {
        std::cout << "Something happened...n";
        return 1;
    }
}

Мы увидим такой вывод:

Logger(): 1
Welcome to f()!
Logger(): 2
~Logger(): 2
~Logger(): 1
Something happened...

Сначала создаётся объект y в блоке try. Затем мы входим в функцию f. В ней создаётся объект x. После этого происходит исключение. Мы должны досрочно покинуть функцию. В этот момент начинается свёртка стека (stack unwinding): вызываются деструкторы для всех созданных объектов в самой функции и в блоке try, как если бы они вышли из своей области видимости. Поэтому перед обработчиком исключения мы видим вызов деструктора объекта x, а затем — объекта y.

Аналогично, свёртка стека происходит и при генерации исключения в конструкторе. Напишем класс с полем Logger и сгенерируем нарочно исключение в его конструкторе:

#include <exception>
#include <iostream>

class C {
private:
    Logger x;

public:
    C() {
        std::cout << "C()n";
        Logger y;
        // ...
        throw std::exception();
    }

    ~C() {
        std::cout << "~C()n";
    }
};

int main() {
    try {
        C c;
    } catch (const std::exception&) {
        std::cout << "Something happened...n";
    }
}

Вывод программы:

Logger(): 1  // конструктор поля x
C()
Logger(): 2  // конструктор локальной переменной y
~Logger(): 2  // свёртка стека: деструктор y
~Logger(): 1  // свёртка стека: деструктор поля x
Something happened...

Заметим, что деструктор самого класса C не вызывается, так как объект в конструкторе не был создан.

Механизм свёртки стека гарантирует, что деструкторы для всех созданных автоматических объектов или полей класса в любом случае будут вызваны. Однако он полагается на важное свойство: деструкторы самих классов не должны генерировать исключений. Если исключение в деструкторе произойдёт в момент свёртки стека при обработке другого исключения, то программа аварийно завершится.

Пример с динамической памятью

Подчеркнём, что свёртка стека работает только с автоматическими объектами. В этом нет ничего удивительного: ведь за временем жизни объектов, созданных в динамической памяти, программист должен следить самостоятельно. Исключения вносят дополнительные сложности в ручное управление динамическими объектами:

void f() {
    Logger* ptr = new Logger();  // конструируем объект класса Logger в динамической памяти
    // ...
    g();  // вызываем какую-то функцию
    // ...
    delete ptr;  // вызываем деструктор и очищаем динамическую память
}

На первый взгляд кажется, что в этом коде нет ничего опасного: delete вызывается в конце функции. Однако функция g может сгенерировать исключение. Мы не перехватываем его в нашей функции f. Механизм свёртки уберёт со стека лишь сам указатель ptr, который является автоматической переменной примитивного типа. Однако он ничего не сможет сделать с объектом в памяти, на которую ссылается этот указатель. В логе мы увидим только вызов конструктора класса Logger, но не увидим вызова деструктора. Нам придётся обработать исключение вручную:

void f() {
    Logger* ptr = new Logger();
    // ...
    try {
        g();
    } catch (...) {  // ловим любое исключение
        delete ptr;  // вручную удаляем объект
        throw;  // перекидываем объект исключения дальше
    }
    // ...
    delete ptr;

}

Здесь мы перехватываем любое исключение и частично обрабатываем его, удаляя объект в динамической памяти. Затем мы прокидываем текущий объект исключения дальше с помощью оператора throw без аргументов.

Согласитесь, этот код очень далёк от совершенства. При непосредственной работе с объектами в динамической памяти нам приходится оборачивать в try/catch любую конструкцию, из которой может вылететь исключение. Понятно, что такой код чреват ошибками. В главе 3.6 мы узнаем, как с точки зрения C++ следует работать с такими ресурсами, как память.

Гарантии безопасности исключений

Предположим, что мы пишем свой класс-контейнер, похожий на двусвязный список. Наш контейнер позволяет добавлять элементы в хранилище и отдельно хранит количество элементов в некотором поле elementsCount. Один из инвариантов этого класса такой: значение elementsCount равно реальному числу элементов в хранилище.

Не вдаваясь в детали, давайте посмотрим, как могла бы выглядеть функция добавления элемента.

template <typename T>
class List {
private:
    struct Node {  // узел двусвязного списка
        T element;
        Node* prev = nullptr;  // предыдущий узел
        Node* next = nullptr;  // следующий узел
    };

    Node* first = nullptr;  // первый узел списка
    Node* last = nullptr;  // последний узел списка
    int elementsCount = 0;

public:
    // ...

    size_t Size() const {
        return elementsCount;
    }

    void PushBack(const T& elem) {
        ++elementsCount;

        // Конструируем в динамической памяти новой узел списка
        Node* node = new Node(elem, last, nullptr);

        // Связываем новый узел с остальными узлами
        if (last != nullptr) {
            last->next = node;
        } else {
            first = node;
        }
        last = node;
    }
};

Не будем здесь рассматривать другие функции класса — конструкторы, деструктор, оператор присваивания… Рассмотрим функцию PushBack. В ней могут произойти такие исключения:

  1. Выражение new может сгенерировать исключение std::bad_alloc из-за нехватки памяти.

  2. Конструктор копирования класса T может сгенерировать произвольное исключение. Этот конструктор вызывается при инициализации поля element создаваемого узла в конструкторе класса Node. В этом случае new ведёт себя как транзакция: выделенная перед этим динамическая память корректно вернётся системе.

Эти исключения не перехватываются в функции PushBack. Их может перехватить код, из которого PushBack вызывался:

#include <iostream>

class C;  // какой-то класс

int main() {
    List<C> data;
    C element;

    try {
        data.PushBack(element);
    } catch (...) {  // не получилось добавить элемент
        std::cout << data.Size() << "n";  // внезапно 1, а не 0
    }

    // работаем дальше с data
}

Наша функция PushBack сначала увеличивает счётчик элементов, а затем выполняет опасные операции. Если происходит исключение, то в классе List нарушается инвариант: значение счётчика elementsCount перестаёт соответствовать реальности. Можно сказать, что функция PushBack не даёт гарантий безопасности.

Всего выделяют четыре уровня гарантий безопасности исключений (exception safety guarantees):

  1. Гарантия отсутствия сбоев. Функции с такими гарантиями вообще не выбрасывают исключений. Примерами могут служить правильно написанные деструктор и конструктор перемещения, а также константные функции вида Size.

  2. Строгая гарантия безопасности. Исключение может возникнуть, но от этого объект нашего класса не поменяет состояние: количество элементов останется прежним, итераторы и ссылки не будут инвалидированы и т. д.

  3. Базовая гарантия безопасности. При исключении состояние объекта может поменяться, но оно останется внутренне согласованным, то есть, инварианты будут соблюдаться.

  4. Отсутсвие гарантий. Это довольно опасная категория: при возникновении исключений могут нарушаться инварианты.

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

Переместим в нашей функции PushBack изменение счётчика в конец:

    void PushBack(const T& elem) {
        Node* node = new Node(elem, last, nullptr);

        if (last != nullptr) {
            last->next = node;
        } else {
            first = node;
        }
        last = node;

        ++elementsCount;  // выполнится только если раньше не было исключений
    }

Теперь такая функция соответствует строгой гарантии безопасности.

В документации функций из классов стандартной библиотеки обычно указано, какой уровень гарантии они обеспечивают. Рассмотрим, например, гарантии безопасности класса std::vector.

  • Деструктор, функции empty, size, capacity, а также clear предоставляют гарантию отсутствия сбоев.

  • Функции push_back и resize предоставляют строгую гарантию.

  • Функция insert предоставляет лишь базовую гарантию. Можно было бы сделать так, чтобы она предоставляла строгую гарантию, но за это пришлось бы заплатить её эффективностью: при вставке в середину вектора пришлось бы делать реаллокацию.

Функции класса, которые гарантируют отсутсвие сбоев, следует помечать ключевым словом noexcept:

class C {
public:
    void f() noexcept {
        // ...
    }
};

С одной стороны, эта подсказка позволяет компилятору генерировать более эффективный код. С другой — эффективно обрабатывать объекты таких классов в стандартных контейнерах. Например, std::vector<C> при реаллокации будет использовать конструктор перемещения класса C, если он помечен как noexcept. В противном случае будет использован конструктор копирования, который может быть менее эффективен, но зато позволит обеспечить строгую гарантию безопасности при реаллокации.

Project
/ Compile vrun

Компиляция
— это процесс преобразования исходной
программы в исполняемую. Процесс
компиляции состоит из двух этапов. На
первом этапе выполняется проверка
текста программы на отсутствие ошибок,
на втором — генерируется исполняемая
программа (ехе-файл).Сообщения
компилятора бывают трех типов:

  1. Error
    — ошибка

  2. Warning
    — предупреждение

  3. Hint
    — подсказка или совет.

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

Мне
приходилось встречать на некоторых
форумах «дружеские советы» новичкам,
сводившиеся к предложению «не
обращать на эту ерунду внимания, это
оптимизатор у Delphi выделывается.»

Так ли это на самом деле?

При
наличии в проекте ошибок-Errors,
не будет сформирован исполняемый файл
и, волей не волей, ошибки придется
исправлять. Наличие же сообщений Hints
и Warnings
позволяет запускать приложение. Обратите
внимание на окно процесса компиляции
(рис. 1),
в строке «Done» написано не Compiled,
что, в общем-то, ожидалось, а предупреждение
There are warnings.
Несмотря на отсутствие ошибок, проект
откомпилирован с тремя «подсказками»
и пятью «предупреждениями».

Насколько
безопасно не обращать на это внимание?

9. Запуск приложения из среды программирования и из Windows. Ошибки времени выполнения приложения. Создание и изменение значка приложения в ImageEditor.

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

  • Главный
    файл проекта, изначально называется
    PROJECT1.DPR.
    Если необходимо переименовать название
    проекта, нужно перезаписать этот файл,
    выбрав в меню File команду Save Project As… При
    задании имени следует придерживаться
    правил задания имен в Object Pascal. Имя не
    должно содержать пробелов (используйте
    в таком случае знак подчеркивания),
    должно начинаться с буквы (цифры
    допустимы, но со второй позиции), не
    должно содержать других символов, кроме
    букв и цифр. Под каким именем вы сохраняете
    проект, под таким именем и будет
    создаваться исполняемый EXE файл, который
    вы всегда сможете самостоятельно
    переименовать.

  • Модуль
    программы, который автоматически
    появляется в начале работы Unit1. Файл
    называется UNIT1.PAS
    по
    умолчанию, но его можно назвать любым
    другим именем, вроде MAIN.PAS.
    Это делается аналогично переименованию
    названия проекта. Необходимо выбрать
    в меню File команду Save As… Сохраняется
    активный модуль в редакторе кода.

  • Файл
    главной формы, который по умолчанию
    называется UNIT1.DFM,
    используется для сохранения информации
    о внешнем виде формы (окна разрабатываемого
    приложения). При переименовании названия
    модуля автоматически меняет свое
    название.

  • Файл
    PROJECT1.RES
    изначально
    содержит иконку для проекта. Создается
    автоматически. Имеет одноименное
    название с названием проекта.

  • Файл
    PROJECT1.DSK
    содержит
    информацию о состоянии рабочего
    пространства. Состояние окон во время
    разработки приложения.

  • Исполняемый
    EXE
    файл, который создается в процессе
    компиляции проекта.

Разумеется,
если сохранить проект под другим именем,
то изменят название и файлы с расширением
RES
и DSK.

После
компиляции программы получаются файлы
с расширениями:

DCU
— скомпилированные модули PAS для
последующей быстрой компиляции некоторые
файлы не перезаписываются

EXE
— исполняемый файл

~PA,
~DP

backup файлы редактора. Автоматически
создаются и содержат информацию, перед
последним сохранением.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Понравилась статья? Поделить с друзьями:
  • Ошибки ваз 2114 1602 код ошибки
  • Ошибки вольво s60 расшифровка
  • Ошибки врачей при удалении зуба мудрости
  • Ошибки ваз 21124 16 клапанов расшифровка
  • Ошибки вольво s60 на бортовом компьютере