بخش ۱۱ - وراثت و چندریختی

در این بخش رابطه‌ی وراثت بین کلاس‌ها را بررسی می‌کنیم.

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

وراثت در اشخاص

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

01_inheritance.cpp
#include <iostream>
#include <string>
using namespace std;

class Person {
public:
	Person(string n, string c) : name(n), nat_code(c) {}

	string get_name() { return name; }
	string get_nat_code() { return nat_code; }
private:
	string name;
	string nat_code;
};

class Student : public Person {
public:
	Student(string n, string c, string sid)
		: Person(n, c),
		student_id(sid) {}

	string get_id() { return student_id; }
private:
	string student_id;
};

class Employee : public Person {
public:
	Employee(string n, string c, string ec, int bs)
		: Person(n, c), emp_code(ec), base_salary(bs) {}

	string get_emp_code() { return emp_code; }
	
	int calc_salary(int hours_worked) {
		int hourly_pay = base_salary / 240;
		return base_salary + (hours_worked - 240) * hourly_pay * 1.4;
	}
private:
	string emp_code;
	int base_salary;
};

int main()
{
	Student s("gholam", "0123456789", "810188123");
	cout << s.get_name() << endl;

	Employee e("ghamar", "1234567890", "1234", 500000);
   	cout << e.calc_salary(263) << endl;	
}

				
وراثت استاد از کارمند

اساتید دانشگاه کارمندان دانشگاه نیز محسوب می‌شوند. به همین دلیل کلاس مربوطه به عنوان زیرکلاس کارمند تعریف شده است.

02_inheritance.cpp
class Employee : public Person {
public:
	Employee(string n, string c, string ec, int bs)
		: Person(n, c), emp_code(ec), base_salary(bs) {}

	string get_emp_code() { return emp_code; }
	
	int calc_salary(int hours_worked) {
		int hourly_pay = base_salary / 240;
		return base_salary + (hours_worked - 240) * hourly_pay * 1.4;
	}
private:
	string emp_code;
	int base_salary;
};

class Prefessor : public Employee {
public:
	Prefessor(string n, string c, string ec, int bs, int ut)
		: Employee(n, c, ec, bs), units_taught(ut) {}

	int calc_salary(int hours_worked) {
		int extra_units = units_taught - 10;
		return Employee::calc_salary(hours_worked) + extra_units * 50000;
	}
private:
	int units_taught;
};

int main()
{
	Prefessor f("ghodrat", "333333", "1235", 800000, 13);
	cout << f.get_name() << endl;
	cout << f.calc_salary(263) << endl;
}
				
چندریختی

در این مثال، اشکال هندسی (به عنوان نمونه، مستطیل و دایره) در سلسله‌مراتب وراثت از کلاس مجرد «شکل» به ارث می‌برند.

03_shapes.cpp
#include <vector>
#include <cstdlib>
#include <iostream>
using namespace std;

class IllegalArgumentException {};

class Shape {
public:
    Shape(int init_x, int init_y) : x(init_x), y(init_y) {}
    int get_x() const { return x; }
    int get_y() const { return y; }
    void move(int dx, int dy);

    virtual void scale(int s) = 0;
    virtual void print() = 0;
protected:
    int x;
    int y;
};

void Shape::move(int dx, int dy)
{
    x += dx;
    y += dy;
}

class Rect : public Shape {
public:
    Rect(int init_x, int init_y, int w, int h);
    virtual void scale(int s);
    virtual void print();
private:
    int width;
    int height;
};

Rect::Rect(int init_x, int init_y, int w, int h)
    : Shape(init_x, init_y)
{
    if (w <= 0 || h <= 0)
	    throw IllegalArgumentException();
    width = w;
    height = h;
}

void Rect::scale(int s)
{
    width *= s;
    height *= s;
}

void Rect::print()
{
    cout << "Rect: " << x << ',' << y << ',' << width << ',' << height << endl;
}

class Circle : public Shape {
public:
    Circle(int init_x, int init_y, int r);
    virtual void scale(int s);
    virtual void print();
private:
    int radius;
};

Circle::Circle(int init_x, int init_y, int r)
    : Shape(init_x, init_y)
{
    if (r <= 0)
	    throw IllegalArgumentException();
    radius = r;
}

void Circle::scale(int s)
{
    radius *= s;
}

void Circle::print()
{
    cout << "Circle: " << x << ',' << y << ',' << radius << endl;
}

int main()
{
    vector<Shape*> shapes;
    
    while (true) {
        cout << "r. New Rectangle\n";
        cout << "c. New Circle\n";
        cout << "p. Display Shapes\n";
        cout << "m. Move All\n";
        cout << "s. Scale All\n";
        cout << "q. Exit\n";
        
        char ch;    
        cin >> ch;
            
        if (ch == 'r') {
            int x, y, w, h;
            cout << "enter x, y, width, and height: ";
            cin >> x >> y >> w >> h;
            
            Rect *rect = new Rect(x, y, w, h);
            shapes.push_back(rect);
            
        } else if (ch == 'c') {
            int x, y, r;
            cout << "enter x, y, and radius: ";
            cin >> x >> y >> r;
            
            Circle *circ = new Circle(x, y, r);
            shapes.push_back(circ);
        
        } else if (ch == 'p') {
            for (int i = 0; i < shapes.size(); i++)
                shapes[i]->print();
            cout<< endl;

        } else if (ch == 'm') {
            int dx, dy;
            cout << "enter dx, dy: ";
            cin >> dx >> dy;
            for (int i = 0; i < shapes.size(); i++)
                shapes[i]->move(dx, dy);
            
        } else if (ch == 's') {
            int s;
            cout << "enter s: ";
            cin >> s;
            for (int i = 0; i < shapes.size(); i++)
                shapes[i]->scale(s);
        }
        else if (ch == 'q') {
            for (int i = 0; i < shapes.size(); i++)
                delete shapes[i];
            break;
        }
    } // while (true)
}	
				
مرتب‌سازی کلی

در این مثال، تلاش می‌کنیم تابعی باری مرتب‌سازی آرایه‌ها بنویسیم طوری که با تمام انواع داده‌ای کار کند. این نیاز در بخش‌های آینده با استفاده از الگوها (بخش ۱۵) و کتابخانه‌ی STL‌ (بخش ۱۷) به شکل بسیار ساده‌تری حل خواهد شد و این مثال صرفاً حالت آموزشی دارد.

04_PolySort.cpp
#include <iostream>
#include <string>
using namespace std;

class object {
public:
    virtual int compare_to(const object* o) = 0;
    virtual string to_string() const = 0;
};

class incomparable_types_ex {
public:
    incomparable_types_ex(const object* _o1, const object* _o2) : o1(_o1), o2(_o2) {}
    const object* o1;
    const object* o2;
};

class duck : public object {
public:
    duck(string n) : name(n) {}

    int compare_to(const object* o) {
        const duck* p = dynamic_cast<const duck*>(o);
        if (p == NULL)
            throw incomparable_types_ex(this, o);
        return name.compare(p->name);
    }
    string to_string() const { return name; }
private:
    string name;
};

class student : public object {
public:
    student(string n, double d) : name(n), grade(d) {}

    int compare_to(const object* o) {
        const student* p = dynamic_cast<const student*>(o);
        if (p == NULL)
            throw incomparable_types_ex(this, o);
        return grade - p->grade;
    }

    string to_string() const { return name; }
private:
    string name;
    double grade;
};

int min_index(object* array[], int count, int from_index) {
    int min_idx = from_index;
    for (int i = 1; i < count; i++)
        if (array[i]->compare_to(array[min_idx]) < 0)
            min_idx = i;
    return min_idx;
}

void selection_sort(object* array[], int count, int from_index) {
    if (from_index >= count -1)
        return;
    int min_idx = min_index(array, count, from_index);
    
    object* temp = array[min_idx];
    array[min_idx] = array[from_index];
    array[from_index] = temp;

    selection_sort(array, count, from_index + 1);
}

int main() {
    duck donald("Donald");
    student moez("Moez", 2.0);

    try {
        cout << donald.compare_to(&moez);
    } catch (incomparable_types_ex e) {
        cerr << "Failed to compare incomparable objects: " 
            << e.o1->to_string() << " and " << e.o2->to_string() << endl;
    }

    object* studs[] = {
        new student("Masoud", 2.0),
        new student("Gholam", 15.5),
        new student("Ghamar", 17.8)
    };

    selection_sort(studs, sizeof(studs)/sizeof(object*), 0);

    for (int i = 0; i < sizeof(studs)/sizeof(object*); i++) {
        cout << studs[i]->to_string() << endl;
        delete studs[i];
    }
}