بخش ۱۰ - رسیدگی به خطاها

در این بخش نحوه‌ی رسیدگی به خطاها مورد بحث قرار می‌گیرد. با توجه به این که محل کشف خطا با محلی که امکان رفع آن وجود دارد ممکن است متفاوت باشد، نیاز به این است که رخداد خطا را از محل کشف به محل رفع اطلاع دهیم. برای این کار از روش‌های مختلفی می‌توان بهره گرفت مانند استفاده از مقدار برگشتی توابع، متغیرهای سراسری، پارامترهای رد شده با ارجاع و در نهایت استثناها (exceptions) که مناسب‌ترین راه‌حل است.

مثالی که در اینجا مورد بحث قرار می‌گیرد خواندن اطلاعات تعدادی دانشجو از یک فایل و پردازش آنها است. اطلاعات دانشجو شامل نام و تاریخ تولد او می‌شود. در خواندن این اطلاعات ممکن است خطاهایی کشف شود که در همان لحظه نمی‌توان تصمیم مناسب برای رفع آن را اتخاذ کرد.

محتویات فایل ورودی می‌تواند چیزی شبیه به این باشد:

3
Gholam 12/02/1360
Ghamar 12/02/1360
Gholi 14/12/1359

فهرست مثال‌ها

حل مسئله بدون رسیدگی به خطاها

این نسخه از برنامه بدون این که به خطاها رسیدگی کند عمل می‌کند.

01_err_no.cpp
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;

class Date {
public:
    Date(int d, int m, int y) : day(d), month(m), year(y) {}
    void print() { cout << day << '/' << month << '/' << year; } 
private:
    int day, month, year;
};

class Student {
public:
    Student(string n, Date bd) : name(n), bdate(bd) {}
    void print() { cout << name << '\t'; bdate.print(); }
private:
    string name;
    Date bdate;
};

Date read_date(ifstream& input) {
    int d, m, y;
    char ch;
    input >> d >> ch >> m >> ch >> y;
    return Date(d, m, y);
}

Student read_student(ifstream& input) {
    string name;
    input >> name;
    Date bdate = read_date(input);
    return Student(name, bdate);
}

void read_student_info(char* filename, vector<Student>& v) {
    ifstream input(filename);
    int count;
    input >> count;
    for (int i = 0; i < count; i++) {
        Student s = read_student(input);
        v.push_back(s);
    }
    input.close();
}

void do_some_processing(vector<Student>& v) {
    for (int i = 0; i < v.size(); i++) {
        v[i].print();
        cout << endl;
    }
}

int main(int argc, char* argv[]) {
    vector<Student> students;
    read_student_info(argv[1], students);
    do_some_processing(students);
}
				
رسیدگی به خطا با استفاده از متغیر سراسری

این نسخه از برنامه با استفاده از یک متغیر سراسری error برای انتشار بروز خطا استفاده می‌کند. دقت کنید که این نحوه‌ی انتشار خطا اولاً کد را به یک متغیر سراسری وابسته می‌کند. ثانیاً بعد از تمام فراخوانی‌ها لازم است مقدار متغیر مذکور کنترل شود و این کد را بسیار ناخوانا می‌کند.

02_err_global_var.cpp
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <cstdlib>
using namespace std;

int error;

class Date {
public:
    Date(int d, int m, int y);
    void print();
private:
    int day;
    int month;
    int year;
};

bool is_leap_year(int y) {
    int r = y % 33;
    return r==1 || r==5 || r==9 || r==13 || r==17 || r==22 || r==26 || r==30;
}

int days_of_month(int m, int y) {
    if (m < 1 || m > 12) {
        error = 1;
        return 0;
    } else
        error = 0;
    if (m < 7)
        return 31;
    else if (m < 12)
        return 30;
    else
        return is_leap_year(y) ? 30 : 29;
}

Date::Date(int d, int m, int y) {
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y)) {
        error = 1;
        return;
    }
    day = d;
    month = m;
    year = y;
    error = 0;
}

void Date::print() {
    cout << year << '/' << month << '/' << day;
}

Date read_date(ifstream& input) {
    int d, m, y;
    char ch;
    input >> d;
        input >> ch;
    if (ch != '/') {
        error = 1;
        return Date(1,1,1);
    }
        input >> m;
        input >> ch;
    if (ch != '/') {
        error = 1;
        return Date(1,1,1);
    }
    input >> y;
    error = 0;
    return Date(d, m, y);
}

class Student {
public:
    Student(string n, Date bd) : name(n), bdate(bd) {}
    void print() { cout << name << '\t'; bdate.print(); }
private:
    string name;
    Date bdate;
};

Student read_student(ifstream& input) {
    string name;
    input >> name;
    Date bdate = read_date(input);
    if (error == 1)
        return Student("", Date(1,1,1));
    else 
        return Student(name, bdate);
}

void read_student_info(char* filename, vector<Student>& v) {
    ifstream input(filename);
    int count;
    input >> count;
    for (int i = 0; i < count; i++) {
        Student s = read_student(input);
        if (error == 1) {
            cerr << "Error in reading line #" << (i+1) << endl;
            continue;
        }
        v.push_back(s);
    }
}

void do_some_processing(vector<Student>& v) {
    for (int i = 0; i < v.size(); i++) {
        v[i].print();
        cout << endl;
    }
}

int main(int argc, char* argv[]) {
    vector<Student> students;
    read_student_info(argv[1], students);
    do_some_processing(students);
}
				
رسیدگی به خطا با استفاده از استثناها (exceptions)

این نسخه از برنامه با از استثناها برای رسیدگی به خطا کمک می‌گیرد

03_err_exception.cpp

class Bad_Date_Exception {};

Date::Date(int d, int m, int y) {
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y)) {
        throw Bad_Date_Exception();
    }
    day = d;
    month = m;
    year = y;
}

Date read_date(ifstream& input) {
    int d, m, y;
    char ch;
    input >> d;
    input >> ch;
    if (ch != '/') {
        throw Bad_Date_Exception();
    }
    input >> m;
    input >> ch;
    if (ch != '/') {
        throw Bad_Date_Exception();
    }
    input >> y;
    return Date(d, m, y);
}

Student read_student(ifstream& input) {
    string name;
    input >> name;
    Date bdate = read_date(input);
    return Student(name, bdate);
}

void read_student_info(char* filename, vector<Student>& v) {
    ifstream input(filename);
    int count;
    input >> count;
    for (int i = 0; i < count; i++) {
        try {
            Student s = read_student(input);
            v.push_back(s);
        } catch (Bad_Date_Exception ex) {
            cerr << "Error in line #" << (i+1) << endl;
        }
    }
}

int main(int argc, char* argv[]) {
    vector<Student> students;
    read_student_info(argv[1], students);
    do_some_processing(students);
}