ТОП 10:

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



Если в данной программе нам понадобится инициализировать значением объект класса 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; Нарушение авторского права страницы

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