نسخه قابل چاپ

بخش ۷ - مفاهیم پایه‌ی شیءگرایی

در این بخش، تعریف یک کلاس ساده برای نگهداری تاریخ مورد بررسی قرار می‌گیرد. این کلاس طی مثال‌های متوالی کامل‌تر می‌شود.

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

کلاس تاریخ - مرحله‌ی اول

پیاده‌سازی ابتدایی تاریخ در قالب یک کلاس

#include <iostream>
#include <cstdlib>
using namespace std;

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

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || 
        m < 1 || m > 12 || 
        d < 1 || 
        (m < 7 && d > 31) || 
        (m > 6 && m < 12 && d > 30) ||
        (m == 12 && d > 29))
            abort();

    day = d;
    month = m;
    year = y;
}

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

int main()
{
    Date bd;
    bd.print_date();
    bd.set_date(31, 6, 1352);
    bd.print_date();
    // bd.day = 14;
}


					
چه خواهد شد ...؟
کلاس تاریخ - سازنده

اضافه شدن سازنده (constructor) به کلاس تاریخ

#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                
class Date {
public:
    Date(int d, int m, int y);
    // ... other members from the previous example
    void set_date(int d, int m, int y);
    void print_date();
private:
    int day;
    int month;
    int year;
};

Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
void Date::set_date(int d, int m, int y)
{
    if (y < 0 ||
            m < 1 || m > 12 ||
            d < 1 ||
            (m < 7 && d > 31) ||
            (m > 6 && m < 12 && d > 30) ||
            (m == 12 && d > 29))
        abort();
                                                                                
    day = d;
    month = m;
    year = y;
}
                                                                                
void Date::print_date()
{
    cout << day << '/' << month << '/' << year;
}

int main()
{
    // Date bd;
    Date bd(31, 6, 1352);
    bd.print_date();
    cout << '\n';
}
					
کلاس تاریخ - توابع دسترسی

دسترسی به اعضای خصوصی تاریخ از طریق getter ها

#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                
class Date {
public:
    // ... other members from the previous example
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();
                                                                                
    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
void Date::set_date(int d, int m, int y)
{
    if (y < 0 ||
            m < 1 || m > 12 ||
            d < 1 ||
            (m < 7 && d > 31) ||
            (m > 6 && m < 12 && d > 30) ||
            (m == 12 && d > 29))
        abort();
                                                                                
    day = d;
    month = m;
    year = y;
}
                                                                                
void Date::print_date()
{
    cout << day << '/' << month << '/' << year;
}
                                                                            
int main()
{
    Date bd(31, 6, 1352);
    bd.print_date();
    cout << '\n';
    cout << bd.get_day() << endl;
}
					
کلاس تاریخ - توابع کمکی

استفاده از توابع معمولی (نه توابع عضو) به عنوان توابع کمکی

#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                
// ... definition of class Date

class Date {
public:
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 33;
    return r==1 || r==5 || r==9 || r==13 || r==17 || r==22 || r==26 || r==30;
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || 
        m < 1 || m > 12 || 
        d < 1 || 
        (m < 7 && d > 31) || 
        (m > 6 && m < 12 && d > 30) ||
        (m == 12 && !is_leap_year(y) && d > 29) ||
        (m == 12 && is_leap_year(y) && d > 30)) 
            abort();

    day = d;
    month = m;
    year = y;
}
                                                                                
void Date::print_date()
{
    cout << day << '/' << month << '/' << year;
}

int main()
{
    Date bd(31, 6, 1352);
    bd.print_date();
    cout << '\n';
    cout << bd.get_day() << endl;
}

					
کلاس تاریخ - خواناتر کردن اعتبارسنجی

جدا کردن تابع days_of_month به منظور خوانا کردن برنامه

#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                
// ... definition of class Date

class Date {
public:
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 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 < 7)
        return 31;
    else if (m < 12)
        return 30;
    else if (m == 12)
        return is_leap_year(y) ? 30 : 29;
    else  
        abort();
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y))
        abort();

    day = d;
    month = m;
    year = y;
}
                                                                                
void Date::print_date()
{
    cout << day << '/' << month << '/' << year;
}

int main()
{
    Date bd(31, 6, 1352);
    bd.print_date();
    cout << '\n';
    cout << bd.get_day() << endl;
}

					
کلاس تاریخ - جلوبردن تاریخ

اضافه کردن یک تابع عضو برای جلوبردن تاریخ به اندازه‌ی یک روز

#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                

class Date {
public:
// ... definition of other members
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();
    void inc_one_day();

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 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 < 7)
        return 31;
    else if (m < 12)
        return 30;
    else if (m == 12)
        return is_leap_year(y) ? 30 : 29;
    else
        abort();
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y))
        abort();

    day = d;
    month = m;
    year = y;
}

void Date::inc_one_day()
{
    day++;
    if (day > days_of_month(month, year)) {
        day = 1;
        month++;
        if (month > 12) {
            month = 1;
            year++;
        }
    }
}
                                                                                
void Date::print_date()
{
    cout << day << '/' << month << '/' << year;
}

int main()
{
    Date bd(31, 6, 1352);
    bd.print_date();
    cout << '\n';
    cout << bd.get_day() << endl;
}

					
کلاس تاریخ - مقایسه‌ی تاریخ‌ها

مقایسه‌ی تساوی دو تاریخ در قالب یک تابع عضو (equals) و محاسبه‌ی فاصله‌ی دو تاریخ در قالب یک تابع کمکی (days_between) پیاده‌سازی شده‌اند.

#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                

class Date {
public:
// ... definition of other members
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();
    void inc_one_day();
    bool equals(Date d);

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 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 < 7)
        return 31;
    else if (m < 12)
        return 30;
    else if (m == 12)
        return is_leap_year(y) ? 30 : 29;
    else
        abort();
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y))
        abort();

    day = d;
    month = m;
    year = y;
}

void Date::inc_one_day()
{
    day++;
    if (day > days_of_month(month, year)) {
        day = 1;
        month++;
        if (month > 12) {
            month = 1;
            year++;
        }
    }
}

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

bool Date::equals(Date d) {
    return day == d.day && month == d.month && year == d.year;
}

int days_between(Date d1, Date d2) {
    // Assuming d1 is not later than d2
    int count = 1;
    while (!d1.equals(d2)) {
        d1.inc_one_day();
        count++;
    }
    return count;
}

int main()
{
    Date bd(31, 6, 1352);
    Date today(10, 12, 1391);
    cout << days_between(bd, today) << endl;
}
					
کلاس تاریخ - تبدیل رشته به تاریخ

تبدیل یک رشته به تاریخ بدون درنظر گرفتن خطاهای فرمت ورودی

#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
                                                                                
// ... definition of class Date
class Date {
public:
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();
    void inc_one_day();
    bool equals(Date d);

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 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 < 7)
        return 31;
    else if (m < 12)
        return 30;
    else if (m == 12)
        return is_leap_year(y) ? 30 : 29;
    else
        abort();
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y))
        abort();

    day = d;
    month = m;
    year = y;
}

void Date::inc_one_day()
{
    day++;
    if (day > days_of_month(month, year)) {
        day = 1;
        month++;
        if (month > 12) {
            month = 1;
            year++;
        }
    }
}

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

bool Date::equals(Date d) {
    return day == d.day && month == d.month && year == d.year;
}

Date str_to_date(string s) {
    //TODO: Handle formatting errors
    int slash_pos = s.find('/');
    int d = atoi(s.substr(0, slash_pos).c_str());
    s = s.substr(slash_pos + 1);
    slash_pos = s.find('/');
    int m = atoi(s.substr(0, slash_pos).c_str());
    int y = atoi(s.substr(slash_pos + 1).c_str());

    return Date(d, m, y);
}

int main()
{
    string s = "12/01/1359";
    Date d = str_to_date(s);
    d.print_date();

    str_to_date("10/01/1300").print_date();
}
					
کلاس تاریخ - برداری از تاریخ‌ها

می‌توان از کلاس تاریخ به عنوان تایپ عناصر یک بردار استفاده کرد

#include <vector>
#include <iostream>
#include <cstdlib>
using namespace std;
                                                                                
// ... definition of class Date

class Date {
public:
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();
    void inc_one_day();
    bool equals(Date d);

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 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 < 7)
        return 31;
    else if (m < 12)
        return 30;
    else if (m == 12)
        return is_leap_year(y) ? 30 : 29;
    else
        abort();
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y))
        abort();

    day = d;
    month = m;
    year = y;
}

void Date::inc_one_day()
{
    day++;
    if (day > days_of_month(month, year)) {
        day = 1;
        month++;
        if (month > 12) {
            month = 1;
            year++;
        }
    }
}

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

bool Date::equals(Date d) {
    return day == d.day && month == d.month && year == d.year;
}

int main()
{
    Date bd(30, 12, 1387);

    vector<Date> dates;
    dates.push_back(bd);
    dates.push_back(Date(1, 1, 1));
    dates[0].print_date();

    vector<Date> dates2(100, Date(1,1,1));
}
					
ترکیب کلاس‌ها

در این مثال، کلاسی به نام Person تعریف می‌شود که یکی از فیلدهای آن تاریخ تولد (از نوع Date) است.

#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
                                                                                
// ... definition of class Date

class Date {
public:
    Date(int d, int m, int y);
    void set_date(int d, int m, int y);
    void print_date();
    void inc_one_day();
    bool equals(Date d);

    int get_day() { return day; }
    int get_month() { return month; }
    int get_year() { return year; }
private:
    int day;
    int month;
    int year;
};
                                                                                
Date::Date(int d, int m, int y)
{
    set_date(d, m, y);
}
                                                                                
bool is_leap_year(int year)
{
    int r = year % 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 < 7)
        return 31;
    else if (m < 12)
        return 30;
    else if (m == 12)
        return is_leap_year(y) ? 30 : 29;
    else
        abort();
}

void Date::set_date(int d, int m, int y)
{
    if (y < 0 || m < 1 || m > 12 || d < 1 || d > days_of_month(m, y))
        abort();

    day = d;
    month = m;
    year = y;
}

void Date::inc_one_day()
{
    day++;
    if (day > days_of_month(month, year)) {
        day = 1;
        month++;
        if (month > 12) {
            month = 1;
            year++;
        }
    }
}

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

bool Date::equals(Date d) {
    return day == d.day && month == d.month && year == d.year;
}

Date str_to_date(string s) {
    //TODO: Handle formatting errors
    int slash_pos = s.find('/');
    int d = atoi(s.substr(0, slash_pos).c_str());
    s = s.substr(slash_pos + 1);
    slash_pos = s.find('/');
    int m = atoi(s.substr(0, slash_pos).c_str());
    int y = atoi(s.substr(slash_pos + 1).c_str());

    return Date(d, m, y);
}

class Person {
public:
    Person(string n, int d, int m, int y);
    Date get_bdate() { return bdate; }
    string get_name() { return name; }
private:
    string name;
    Date bdate; 
};

Person::Person(string n, int d, int m, int y)
    : bdate(d, m, y)
{
    if (n == "")
        abort();
    name = n;
}

int main()
{
    vector<Person> vp;
    vp.push_back(Person("gholam", 2, 7, 1370));
    vp.push_back(Person("ghamar", 3, 12, 1368));
    vp.push_back(Person("alaleh", 12, 2, 1360));

    cout << "Today? ";
    string today_str;
    cin >> today_str;
    Date today = str_to_date(today_str);

    for (int i = 0; i < vp.size(); i++)
        if (vp[i].get_bdate().equals(today))
            cout << "Happy Birth Day " << vp[i].get_name() << "!\n";
}
					
تمرین‌های کوتاه
  1. یک متد به نام compare به تاریخ اضافه کنید طوری که d1.compare(d2) در حالتی که d1 < d2، مقدار منفی یک، در حالتی که d1 = d2، مقدار صفر و در حالتی که d1 > d2 مقدار یک را برگرداند.
  2. در تعریف تابع days_between فرض شده است که پارامتر اول کوچکتر یا مساوی پارامتر دوم است. تعریف این تابع را طوری تغییر دهید که این محدودیت برداشته شود.
  3. با فرض این که ۱ فروردین ۱ جمعه است، متدی به کلاس تاریخ به نام day_of_week اضافه کنید که مشخص کند تاریخ مربوطه چه روزی از هفته است. مقدار که این تابع برمی‌گرداند یکی از رشته‌های "sun"، "sat" تا "fri" است.
  4. هدف این تمرین طراحی کلاسی به نام Time است که همان طور که از نام آن پیداست، قرار است برای ذخیره کردن زمان استفاده می‌شود. این کلاس را طوری طراحی و پیاده‌سازی کنید که امکانات زیر را داشته باشد:
    • فیلدهای مناسب برای ذخیره سازی دقیقه و ساعت
    • یک سازنده که ورودی آن دقیقه و ساعت باشد
    • یک سازنده که ورودی آن تنها ساعت باشد (در این حالت دقیقه باید صفر در نظر گرفته شود)
    • یک متد برای تغییر دقیقه و ساعت
    • دو متد برای برگرداندن مقدار دقیقه و ساعت
    • یک متد که ورودی آن شیء دیگری از کلاس Time است و مانند تمرین اول دو زمان را با هم مقایسه می‌کند
    • متدی که بر اساس زمان رشته ای برگرداند که بگوید چه وقتی از روز است. فرض کنید از ابتدای یک روز تا ساعت ۱۱:۵۹ صبح، ۱۲:۰۰ ظهر، از ۱۲:۰۱ تا ۱۶:۵۹ بعدازظهر، از ۱۷:۰۰ تا ۱۹:۵۹ عصر و بعد از آن شب محسوب می‌شود.
    نکات:
    • هر جا قرار است زمان ذخیره شده تغییر کند، باید بررسی شود که اعداد وارد شده قابل قبول باشد و در غیر این صورت اجرای برنامه خاتمه یابد.
    • برای آزمودن امکانات این کلاس یک برنامه ساده در تابع main بنویسید.
    • دقت کنید که فیلدهای کلاس نباید به صورت عمومی (public) قابل دسترسی باشند.