Конструкторы производного класса 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Конструкторы производного класса



Если в данной программе нам понадобится инициализировать значением объект класса CountPr (например с2=--с1; с2=++с1;), то компилятор не позволит использовать конструктор базового класса с одним аргументом, потому что в нём возвращаемым типом данных является тип Count. В связи с этим, мы должны написать новый конструктор для производного класса.

class Count { //áàçîâûé êëàññ

};

//---------------------------------------------------------------------------

class CountPr: public Count { // ïðîèçâîäíûé êëàññ

public:

CountPr() { c = 0; }

CountPr(int d) { c = d; }

CountPr operator--() {

--c;

return CountPr(c);

}

};

//---------------------------------------------------------------------------

int main() {

CountPr c1, c2; // îáúåêò ñ1 ïðîèçâîäíîãî êëàññà

cout << "\n c1=" << c1.get(); //увидим c1=0

++c1; ++c1; ++c1;

cout << "\n c1=" << c1.get(); //увидим c1=3

c2=c1;

c2=--c1;

cout << "\n c2=" << c2.get(); //увидим c2=2

getch(); return 0;

}

Существует также альтернативный вариант записи конструктора производного класса:

class CountPr: public Count { // ïðîèçâîäíûé êëàññ

public:

CountPr(): Count() { }

CountPr(int d): Count(d) { }

CountPr operator--() {

--c;

return CountPr(c);

}

};

Здесь использован новый вид записи конструктора производного класса. Конструктор CountPr(): Count() вызывает конструктор базового класса Counter, который и выполняет нужные действия.

Когда инициализируется значением объект с2 (с2=--с1;), то при этом вызывается конструктор с одним аргументом класса CountPr, который вызывает соответствующий конструктор с одним аргументом из базового класса. Конструкция CountPr(int d): Count (d) означает, что аргумент "d" будет передан от конструктора CountPr() в Count(), где будет использован для инициализации объекта.

Вопрос Как надо изменить производный класс, чтобы появилась возможность использовать постфиксную операцию инкрементирования.
Ответ class Count { … }; class CountPr: public Count { public: CountPr() { c = 0; } CountPr(int d) { c = d; } // или CountPr(int d): Count(d) { } CountPr operator--() { --c; return CountPr(c); } CountPr operator++() { Count::operator++(); } CountPr operator++(int) { c++; return c; } }; //--------------------------------------------------------------------------- int main() { CountPr c1, c2; cout << "\n c1=" << c1.get(); ++c1; ++c1; ++c1; cout << "\n c1=" << c1.get(); c2=++c1; c2=c1++; cout << "\n c2=" << c2.get(); getch(); return 0; }  
Вопрос Какой метод надо добавить в производный класс, чтобы в главной функции, появилась возможность использовать оператор c3 = c2 * c1; или c3 = CountPr(10) * c1?
Ответ CountPr operator*(CountPr a) { return CountPr(c*a.c); }

 

Перегрузка функций

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

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

class Stack {

protected:

enum { MAX = 3 }; // размер стека

int st[MAX]; // данные, хранящиеся в стеке

int top; // индекс последнего элемента в стеке

public:

Stack() // конструктор

{ top = -1; }

void push(int var) // помещение числа в стек

{ st[++top] = var; }

int pop() // извлечение числа из стека

{ return st[top--]; }

};

//---------------------------------------------------------------------------

class Stack2: public Stack {

public:

void push(int var) { // помещение числа в стек

if (top > MAX-1) cout << "\nError: stack polon!"; // если стек полон, то ошибка

Stack::push (var); // если всё в порядке, то вызов функции push класса Stack

}

int pop() { // извлечение числа из стека

if (top < 0) cout << "\nError: stack pust!\n"; // если стек пуст, то ошибка

return Stack::pop(); // если всё в порядке, то вызов функции pop класса Stack

}

};

//---------------------------------------------------------------------------

int main() {

Stack2 s1;

s1.push(11); // поместим в стек несколько чисел

s1.push(22);

s1.push(33);

cout << endl << s1.pop(); // заберем числа из стека

cout << endl << s1.pop();

cout << endl << s1.pop();

cout << endl << s1.pop(); //!!!, а данных-то больше нет

return 0; }

Как видно из данного примера, в классе Stack2 содержатся два метода push() и pop(), которые имеют те же имена, аргументы и возвращаемые значения, что и методы базового класса. В связи с этим возникает вопрос: Как компилятор поймёт, какой из методов вызвать? Существует правило: если один и тот же метод существует и в базовом, и в производном классе, то будет выполнен метод производного класса. Это правило справедливо только для объектов производного класса и не справедливо для объектов базового класса, поскольку им ничего не известно о производном классе, поэтому они всегда пользуются методами базового класса.

В нашем примере, в связи с тем что s1 – это объект производного класса Stack2, будет выполнен метод push() класса Stack2. Если стек полон, то метод пошлёт сообщение об ошибке, если – нет, то посредством строки Stack::push (var); вызывается метод push() базового класса Stack.

Иерархия классов

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

В качестве примера рассмотрим базу данных служащих некоторой компании, в которой существует только две категории служащих: менеджеры, занятые управлением, и рабочие, занятые изготовлением товара. В базе данных хранятся имена служащих всех категорий и их идентификационные номера. В информации о менеджерах содержится ещё название их должности и заработная плата, а в информации о рабочих – квалификационный разряд.

 
 

 


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

const int L = 80;

//---------------------------------------------------------------------------

class Sotrudnik { //áàçîâûé êëàññ

private:

char name[L];

int number;

public:

void getdata() {

cout << "\n Vvedite FIO: "; cin.getline(name, L);

cout << " Vvedite nomer: "; cin >> number;

}

void show() const {

cout << "\n Familiya: " << name;

cout << "\n Nomer: " << number;

}

};

//---------------------------------------------------------------------------

class Manager: public Sotrudnik { //ïðîèçâîäíûé êëàññ

private:

char dolgnost[L];

double oklad;

public:

void getdata() {

Sotrudnik::getdata();

cout << " Vvedite dolgnost: "; cin >> dolgnost;

cout << " Vvedite zarplatu: "; cin >> oklad;

}

void show() const {

Sotrudnik::show();

cout << "\n Dolgnost: " << dolgnost;

cout << "\n Zar. plata: " << oklad;

}

};

//---------------------------------------------------------------------------

class Worker: public Sotrudnik {

private:

int raz;

public:

void getdata() {

Sotrudnik::getdata();

cout << " Vvedite razryad: "; cin >> raz;

}

void show() const {

Sotrudnik::show();

cout << "\n Razryad: " << raz;

}

};

//---------------------------------------------------------------------------

int main() {

Manager m1;

Worker r1, r2;

cout << "\nVvod managerov: "; m1.getdata(); cin.get();

cout << "\nVvod workerov: "; r1.getdata(); cin.get(); r2.getdata();

cout << "\nVyvod managerov: "; m1.show();

cout << "\nVyvod workerov: "; r1.show(); r2.show();

getch(); return 0;

}

Обратите внимание на то, что данные базового класса объявлены с ключевым словом private. Это вызвано тем, что производным классам нет необходимости использовать эти данные напрямую. Они используют вызов методов базового класса, которые в свою очередь имеют полный доступ к данным.

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

Вопрос Что надо добавить в эту программу, чтобы появилсь возможность инициализировать в программе объект класса Worker. int main() { … Worker r1, r2("Petrov T.I.", 333, 1); cout << "\nVvod workerov: "; r1.getdata(); cout << "\nVyvod workerov: "; r1.show(); r2.show(); getch(); return 0; }
Ответ class Sotrudnik { private: char name[L]; int number; public: Sotrudnik() { strcpy(name, "\0"); number = 0; } Sotrudnik(char *fio, int n) { strcpy(name, fio); number = n; } void getdata() { cout << "\n Vvedite FIO: "; cin.getline(name, L); cout << " Vvedite nomer: "; cin >> number; } void show() const { cout << "\n Familiya: " << name; cout << "\n Nomer: " << number; } }; //--------------------------------------------------------------------------- class Manager: public Sotrudnik { … }; //--------------------------------------------------------------------------- class Worker: public Sotrudnik { private: int raz; public: Worker(): Sotrudnik() { } Worker(char *wname, int num, int r): Sotrudnik(wname, num) { raz = r; } // или Worker(char *wname, int num, int r): Sotrudnik(wname, num), raz(r) { } void getdata() { Sotrudnik::getdata(); cout << " Vvedite razryad: "; cin >> raz; } void show() const { Sotrudnik::show(); cout << "\n Razryad: " << raz; } };  

Варианты написания программы

Вариант 2

При определении производных классов Manager и Worker можно было-бы использовать спецификатор private (частное наследование), например:

class Manager: private Sotrudnik {…}

class Worker: private Sotrudnik {…}

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

Следует понимать, когда можно использовать частное наследование, а когда – нельзя. Так, если объекту производного класса необходимо использовать непроизводный метод базового класса напрямую, то в этом случае необходимо применять общее наследование (public). Например:

class Manager: public Sotrudnik {

private:

char dolgnost[L];

double oklad;

public:

void getdata() {

Sotrudnik::getdata();

cout << " Vvedite dolgnost: "; cin >> dolgnost;

cout << " Vvedite zarplatu: "; cin >> oklad;

}

// void show() const {…} отсутствует этот метод

};

int main() {

Manager m1;

cout << "\nVyvod managerov: "; m1.show(); // вызовется метод базового класса

}

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

Вариант 3

Здесь, в главной функции для ввода данных о первом рабочем мы использовали оператор r1.getdata();. Если в методе getdata() производного класса убрать вызов одноимённого метода базового класса, то тогда в главной функции вместо оператора r1.getdata(); надо было-бы написать следующее: r1.Sotrudnik::getdata(); r1.getdata();. Только в этом случае обязательно необходимо использовать общее наследование, т.е. class Worker: public Sotrudnik { … };.

 



Поделиться:


Последнее изменение этой страницы: 2016-04-08; просмотров: 344; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.117.81.240 (0.044 с.)