Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Исключения


Автор: Xenon 11.6.2006, 20:56
Немного не понимаю, для чего они, собственно, нужны в некоторых случаях ...
Ну вот у меня метод из классы, для записи файла
Код

void album::write()
{
    ofstream outfile;
    outfile.open("collection.dat",ios::binary | ios::app);
    cout << "Writing to file ...\n";
    if (!outfile)
    {
        cout << "Error";
    }
    for (int j=0;j<n;j++)
    {
        outfile.write((char*)(albums[j]),sizeof(album));
    }
    if (outfile.good()==1)
    {
        cout << "Completed";
    }
    is_changed=false;
}


Если ошибку обработать при помощи исключений, то получится:
Код

void album::write()
{
    ofstream outfile;
    outfile.open("collection.dat",ios::binary | ios::app);
    cout << "Writing to file ...\n";
    if (!outfile)
    {
        throw Error();
    }
    for (int j=0;j<n;j++)
    {
        outfile.write((char*)(albums[j]),sizeof(album));
    }
    if (outfile.good()==1)
    {
        cout << "Completed";
    }
    is_changed=false;
}

И какое тогда премущество ? То, что вся обработка ошибка запихнута в catch а не раскадинана по методам классов и только?
Не понимаю зачем они нужны ... Приведите, если не сложно, примеры, а то я чего-то запутался smile 

Автор: Void 11.6.2006, 20:58
Исключения нужны, чтобы обрабатывать ошибку не на том уровне логики, где она возникает, а выше, где приложению ясно, что с ней делать. 

Автор: MAKCim 11.6.2006, 21:03
исключения - унифицированный способ обработки ошибок
при использовании исключений получаем все претмущества классов (в частности полиморфизм) 

Автор: Xenon 11.6.2006, 21:15
Хм ... а пример? Приведите пример, где это удобнее, чтобы очевидно почувствовать разницу.
PS. По правилам хорошего кодинга для каждого класса нужно создавать один класс, например, Error, для схватки ошибок, или же лучше ошибки разбивать на разные группы? 

Автор: Dray 11.6.2006, 21:38
Хотелось бы добавить, что исключения - единственный оптимальный вареант для обработки ошибок, возникающих в конструкторах. 

Автор: MAKCim 12.6.2006, 21:09
Цитата

Хм ... а пример? Приведите пример, где это удобнее, чтобы очевидно почувствовать разницу.

имхо, в любом случае удобнее 

Автор: Xenon 12.6.2006, 21:13
MAKCim, Ну вот в том варианте, что я кинул, имхо, нет smile  

Автор: Daevaorn 12.6.2006, 21:26
Цитата(XenonSk @  12.6.2006,  22:13 Найти цитируемый пост)
Ну вот в том варианте, что я кинул, имхо, нет 

Зато как хорошо вызывающему коду отлавливать ошибку записи, а? Плюс безопасность исключений требует не просто применения throw/catch, но и изменение дизайна класса/функции. При грамотном использовании удобство чувствуется очень сильно - упрощает жизнь.
 

Автор: MAKCim 12.6.2006, 21:27
Цитата

Ну вот в том варианте, что я кинул, имхо, нет 

ну да,
Код

if (!outfile)
    {
        cout << "Error";
    }

получается что если !outfile то выводится сообщение об ошибке и программа выполняется дальше?
outfile не открылся а туда что-то записывается?
здесь как раз вариант с исключениями правильнее  

Автор: Dray 12.6.2006, 21:38
Все опять же зависит от случаяя. Исключения все равно не стоит использовать везде подряд, например если нужно обрабатывать ввод пользователя по каким-то ограничениям, то исключения вряд ли будут логичным решением. А вот например низкоуровневые вещи, о которых пользователь знать не должен, сообщения об ошибках которые адресованы разработчику, лучше генерировать исключениями.
Еще пример можно привести - это написание различных библиотек, где предпологается, что ими будут пользоваться разработчики которые не знают об их устройстве. Чтобы руководить их действиями и указывать на то что они не правильно используют тот или иной класс, удобней использовать исключения.
Это ВсЁ моЁ ИМХО! 

Автор: Xenon 12.6.2006, 21:45
Dray, Ну, то есть, ты предлагаешь юзать только, фактически, badalloc?

MAKCim, Ну тут же я открываю файл и знаю, если произошла ошибка, то, скорее всего, это связано с некотрыванием файла smile Если я в него пишу данные, то, скорее всего,  с тем, что они туда не пишутся. Не понимаю в чем выгода, хоть убей ... И что значит правильнее?
Ну вот вся прога ... Я только в некоторых местах воткнул исключения и не понял, чего они мне тут дают. Говорю, писали, что преимущество в том, что ты не знаешь, какая ошибка, но ведь это можно на месте проверить в том же методе?
Код

//albums.h
#include <iostream>
#include <iomanip>
#include <string>    
#include <fstream>

const int MAX=30;
using namespace std;
///////////////////////////////////////////////////////
class album    
{    
private:    
    int year;    
    string name;    
    string artist;
    string genre;    
    int cd_num;    
    static album* albums[];
    static int n;
public:    
    static bool is_changed;
    class Error {};
    void enter_data();
    void get_data();
    static void write();//
    static void edit_data();//Коррекция альбома
    static void add();//Добавление альбома
    static void display_all();//Отображение всех альбомов
    static void read(); //Чтение из файла
};

Код

//albums.cpp
#include "albums.h"

///////////////////////////////////////////////////////
///Статические переменные
int album::n;
album* album::albums[MAX];
bool album::is_changed=false;
///////////////////////////////////////////////////////
///Отображение всех альбомов
void album::display_all()
{
    if (n==0)
    {
        cout << "\nThere are no albums\n";
    }
    else
    {
        for (int j=0;j<n;j++)
        {
            albums[j]->get_data();
        }
    }
}
///////////////////////////////////////////////////////
///Добавление альбома
void album::add()
{
    is_changed=true;
    albums[n]=new album;
    albums[n++]->enter_data();
}
///////////////////////////////////////////////////////
///Ввод данных о компакте с клавиатуры
void album::enter_data()    
{    
    cout << "Enter Artist: ";
    cin >> artist;
    cout << "Enter Album: ";    
    cin >> name;    
    cout << "Enter Album`s year ";    
    cin >> year;    
    cout << "Enter Genre: ";    
    cin >> genre;    
}    
/////////////////////////////////////////////////////
///Редактирование альбома
void album::edit_data()    
{
    int i;
    cout << "Enter cd number: ";
    cin >> i;
    albums[i]->enter_data();
}
/////////////////////////////////////////////////////
///Вывод данных об альбоме
void album::get_data()    
{    
    cout << artist << " - " << name << endl;
    cout << "Genre: " << genre << endl;    
    cout << "Year: " << year << endl;    
    cout << "Cd num: " << cd_num << endl;    
}
///////////////////////////////////////////////////////
///Запись в файл данных из ПО
void album::write()
{
    ofstream outfile;
    outfile.open("collection.dat",ios::binary | ios::app);
    cout << "Writing to file ...\n";
    if (!outfile)
    {
        throw Error();
    }
    for (int j=0;j<n;j++)
    {
        outfile.write((char*)(albums[j]),sizeof(album));
    }
    if (outfile.good()==1)
    {
        cout << "Completed";
    }
    is_changed=false;
}
///////////////////////////////////////////////////////
///Чтение данных из файла
void album::read()
{
    cout << "Reading file ... ";
    ifstream inf;
    inf.open("collection.dat",ios::binary);
    if (!inf)
    {
        throw Error();
    }
    else
    {
        n=0;
        while (!inf.eof())
        {
            albums[n]=new album;
            inf.read((char*)(albums[n]),sizeof(album));
            if (inf.eof())
            {
                cout << "Completed";
                break;
            }
            n++;    
        }
    }
}
///////////////////////////////////////////////////////

Код

//main.cpp
#include "albums.h"
#include <conio.h>


int main(int argc, char* argv[])
{    
    int choice;
    do
    {
        try
        {
            cout << "\nWhat to Do? \n1)Display all albums; \n2)Display album; \n3)Add album; \n4)Edit data \n5)Read from file \n6)Save to file \n7)Exit: ";
            cin >> choice;
            switch (choice)
            {
            case 1: album::display_all();
                break;
            case 2: cout << "Enter albums number: "; 
                break;
            case 3: album::add();
                break;
            case 4:album::edit_data();
                break;
            case 5: album::read();
                break;
            case 6: album::write();
                break;
            case 7: break;
            default:cout << "Incorrect choice. Try again";
                break;
            }
        }
        catch (album::Error)
        {
            cout << "Error" << endl;
        }
    }while(choice!=7);
    if (album::is_changed==true)
    {
        do
        {
            char choice;
            cout << "Yo have made changes to file. Do you want to save them? y/n ";
            cin >> choice;
            if (choice=='y' || choice=='Y')
            {
                album::write();
                return false;
            }
            if (choice=='n' || choice=='N')
            {
                return false;
            }
        }while(true);
    }
    _getch();    
    return 0;    
}
  

Автор: Fazil6 12.6.2006, 22:11
Цитата

 Говорю, писали, что преимущество в том, что ты не знаешь, какая ошибка, но ведь это можно на месте проверить в том же методе?

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

 

Автор: Xenon 12.6.2006, 22:48
Fazil6, То есть фишка, как я и писал, только в том, что обработка ошибок организуется - пихается куда хочется. 

Автор: Dray 12.6.2006, 22:50
Страуструп сказал надо использовать исключения и всё! Дальше вопрос можно закрывать...  smile  smile  smile 

...Вряд ли найдется человек, который впервые узнав про указатели сразу понял зачем они. Я вот, например, пока не изучил динамические структуры данных так и не понимал зачем эти указатели. А сейчас уже понимаю, что они и не только для связных списков и прочего нужны. С исключениями тоже самое. Их приимущество скорее всего почувствуется позже, в какой-нибудь конкретной ситуации. 

Автор: Xenon 12.6.2006, 22:53
Dray, Не знаю, я не так и долго изучаю C++, да и вообще, как прошел тему про указатели сразу понял что к чему и зачем они нужны, видать книга хорошая. Но искоючения там вскольз описаны, поэтому я жутко торможу, но вроде исходя из постов других я понял, что мои предположении оказались правильными smile 

Автор: Void 12.6.2006, 22:56
Цитата(Dray @  13.6.2006,  00:50 Найти цитируемый пост)
Страуструп сказал надо использовать исключения и всё!

Не извращай слова Мастера! smile

Совет №2 из главы 14 («Исключения») TC++PL:
Цитата
Don’t use exceptions where more local control structures will suffice;
 

Автор: Xenon 12.6.2006, 23:03
Void Блин, опять все на усмотрение программиста ... Путает новичка такое smile 

Автор: Dray 12.6.2006, 23:06
Цитата(Void @  12.6.2006,  22:56 Найти цитируемый пост)
Не извращай слова Мастера!

Конечно радикально высказался, извеняюсь.  smile  Вот открыл главу 14 советы 1, 2. Как раз говорят о том, что всему свое место. Если где-то кажется нелогичным использование исключений, то лучше их не использовать. 

Автор: Void 12.6.2006, 23:10
Исключения должны служить своей главной цели: обработке ошибок, исключительных ситуаций. Использовать их для передачи управления, строить на них логику не стоит.
Цитата(Dray @  13.6.2006,  01:06 Найти цитируемый пост)
всему свое место

По-моему, эта мысль красной нитью проходит через все стоящие книги по C++. А уж в TC++PL так вообще основная идея в этом smile 

Автор: sergejzr 13.6.2006, 00:25
Один из простых примеров - исключение по нехватке памяти. Не было бы его, пришлось бы перед каждым new проверять, хватит ли памяти на новый обьект. Так как new может в программе быть очень много, легче поймать исключение в критическом отрезке кода.

Ещё удобная вещь, что исключения можно "ловить" разные и они сами расскажут тебе, что произошло. Не надо каждый раз проверять коды возврата итд. Экономит кучу писанины.

Вот например..
Код

// Код на JAVA. Смысл в С++ и Ява примерно одинаков, а синтаксиса С++ я не припомню сейчас
try
{
 storeGameState(somestring);

}catch(NullPointerException e)
{
  System.out.println("Имя файла=null");
  e.printStackTrace():
}catch(FileNotFoundException e)
{
  System.out.println("Файл "+somestring+" не найден");
  e.printStackTrace():
}catch(FileWriteexception e)
{
  System.out.println("Файл "<<somestring<<" защищён от записи");
  e.printStackTrace():
}


Добавлено @ 00:32 
Записывается состояние игры. Там может быть куча разных ошибок. Смысл любой из них, что состояние не сохранено. Но откуда об этом знать самой функции "openFile". Она то не в курсе, зачем файл открывает. 

openFile - знает только, открыля файл или нет
storeGameState- знает, записалось ли состояние игры
итд. Всё выше по абстрактой лестнице от деталей к общему.
  

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)