Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Простое наследование






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

class employee

{
public:
employee(char *, char *, float);
void show_employee(void);
private:
char name[64];
char position[64];
float salary;
};

Далее предположим, что вашей программе требуется класс manager, который добавляет следующие элементы данных в класс employee:

float annual_bonus;
char company_car[64];
int stock_options;

В данном случае ваша программа может выбрать два варианта: во-первых, программа может создать новый класс manager, который дублирует многие элементы класса employee, или программа может породить класс типа manager из базового класса employee. Порождая класс manager из существующего класса employee, вы снижаете объем требуемого программирования и исключаете дублирование кода внутри вашей программы.

Для определения этого класса вы должны указать ключевое слово class, имя manager, следующее за ним двоеточие и имя employee, как показано ниже:

Производный класс //-----> class manager: public employee { < -------// Базовый класс

// Здесь определяются элементы
};

Ключевое слово public, которое предваряет имя класса employee, указывает, что общие (public) элементы класса employee также являются общими и в классе manager. Например, следующие операторы порождают класс manager.

class manager: public employee

{
public:
manager(char *, char *, char *, float, float, int);
void show_manager(void);
private:
float annual_bonus;
char company_car[64];
int stock_options;
};

 

class A

{

};

 

class B: public A //класс B наследуется из класса A

{

};

 

A obj1; //obj1 есть объект класса A

B obj2; //obj2 есть объект класса B

 

void main()

{

return;

}

 

................

lass A

{

int x; //x является элементом класса A и доступен только внутри своего класса

public:

void get_x(int); //Прототип функции, через которую в вышеобъявленный x присваивается значение

void show_x(); //Прототип функции, выводящей x на экран

};

class B: public A //класс B наследуется из класса A

{

};

//Определяю функции из класса вне своего класса.

void A:: get_x(int X)

{

x=X; //x и X различны. т.к. регистр разный. x- элемент класса, в X принимается параметр

return;

}

 

void A:: show_x()

{

cout< < x< < endl; //Вывод x на экран

}

A obj1; //obj1 есть объект класса A

B obj2; //obj2 есть объект класса B

void main()

{

clrscr();

int value=10; //value будет передаваться в функцию в качестве параметра

obj1.get_x(value); //Эстафета по классу A: value-> X X-> x

obj1.show_x(); //x из Класса A выводится на экран

//свойства и функциональность родительского класса заимствуются новым классом

value=20;

obj2.get_x(value); //Эстафета по классу B: value-> X X-> x

obj2.show_x(); //x из класса B выводится на экран у

getch();

return;

}

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

Коротко так: Если внутри потомка ничего не написано, то Потомок=Родитель

 

· Наследование – создание подкласса на основе уже существующего

· Класс от которого произошло наследование называется базовым или родителем

· Класс, который произошел от родителя называется производным классом или потомком

· Наследование похоже на доведение чего-то готового до ума через добавление дополнительной функциональности

· Конструкция наследования
class ИмяКлассаПотомка: метод доступа ИмяКлассаРодителя {методы потомка};

· При использовании наследования свойства и функциональность родительского класса заимствуются классом-потомком

· В случае если в класс потомок добавлены какие-то элементы, то класс потомок не делится этим с родителем

· При использовании в методах класса-потомка, одноименного метода с методом класса-родителя происходит что-то вроде отказа от этой части наследования и такой метод воспринимается классом потомком как самостоятельный и свой собственный метод

· Несмотря на то, что класс потомок может быть равен классу-родителю, Нет и не может быть такого случая когда можно сказать, что класс потомок унаследовал абсолютно всё от класса родителя (прим. В контексте этого вывода: Для работы с классом обязателен объект)

 

ПРОИЗВОДНЫЕ КЛАССЫ

КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ В ПРОИЗВОДНЫХ КЛАССАХ

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

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

#include < iostream.h>
class Base {
public:
Base () {cout < < " \nBase created\n"; }
};
class D_class1(): public Base {
public:
D_class1() {cout < < " D_class1 created\n"; }
};
int main()
{
D_class1 d1;
// ничего не делается, но выполняются конструкторы
return 0;
}

Эта программа создает объект типа D_class1. Она выводит на экран следующий текст:

Base created
D_class1 created

 

Здесь d1 является объектом типа D_class1, производным класса Base. Таким образом, при созда­нии объекта d1 сначала вызывается конструктор Base(), а затем вызывается конструктор D_class1().

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

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

#include < iostream.h>
class Base {
public:
Base() {cout < < " \nBase created\n"; }
~Base() {cout < < " Base destroyed\n\n, "; }
};
class D_class1(): public Base {
public:
D_class1() {cout < < " D_class1 created\n"; }
~D_class1() {cout < < " D_class1 destroyed\n "; }
};
int main()
{
D_class1 d1;
cout < < " \n";
return 0;
}

Эта программа выдаст следующий текст на экран:

Base created
D_class1 created
D_class1 destroyed
Base destroyed

 

Производный класс может служить базовым классом для создания следующего производного класса. Когда такое происходит, конструкторы исполняются в порядке наследования, а деструк­торы — в обратном порядке. В качестве примера рассмотрим следующую программу, использую­щую класс D_class2, производный от класса D_ciass1:

#include < iostream.h>
class Base {
public:
Base() {cout < < " \nBase created\n"; }
~Base() {cout < < " Base destroyed\n\n"; }
};
class D_class1(): public Base {
public:
D_class1() {cout < < " D_class1 created\n"; }
~D_class1() {cout < < " D_class1 destroyed\n "; }
};
class D_class2: public D_class1 {
public:
D_class2() {cout < < " D_class2 created\n"; }
~D_class2() {cout < < " D_class2 destroyed\n "; }
};
int main()
{
D_class1 d1;
D_class2 d2;
cout < < " \n";
return 0;
}

Эта программа выдаст следующий результат:

Base created
D_class1 created
Base created
D_class1 created
D_class2 created
D_class2 destroyed
D_class1 destroyed
Base destroyed
D_class1 destroyed
Base destroyed

 

СТАТУСЫ ДОСТУПА ПРИ НАСЛЕДОВАНИИ

От того, с каким спецификатором доступа объявляется наследование базового класса, зависит статус доступа к членам производного класса. Общая форма наследования классов имеет следую­щий вид:

class имя_класса: доступ имя_класса {
....
};

Здесь доступ определяет, каким способом наследуется базовый класс. Спецификатор доступ может принимать три значения — private, public и protected. В случае, если спецификатор дос­туп опущен, то по умолчанию подразумевается на его месте спецификатор public. Если специ­фикатор доступ принимает значение public, то все публичные и защищенные члены базового класса становятся соответственно публичными и защищенными членами производного класса. Если спецификатор доступ имеет значение private, то все публичные и защищенные члены базо­вого класса становятся частными членами производного класса. Если спецификатор доступ при­нимает значение protected, то все публичные и защищенные члены базового класса становятся защищенными членами производного класса. Для того чтобы усвоить все эти преобразования, рассмотрим пример:

#include < iostream.h>
class X {
protected:
int i;
int j;
public:
void get_ij () {
cout < < " Enter two numbers: ";
cin > > i > > j;
}
void put_ij() { cout < < i < < " " < < j < < " \n"; }
};
// в классе Y, i и j класса X становятся защищенными членами
class Y: public X {
int k;
public:
int get_k() { return k; }
void make_k() { k = i*j; }
};
/* класс Z имеет доступ к i и j класса X, но не к k класса Y, поскольку он является частным */
class Z: public Y {
public:
void f();
};
// i и j доступны отсюда
void z:: f()
{
i = 2; // нормально
j = 3; // нормально
}
int main()
{
Y var;
Z var2;
var.get_ij();
var.put_ij();
var.make_k();
cout < < var.get_k();
cout < < " \n";
var2.f();
var2.put_ij();
return 0;
}

 

Поскольку класс Y наследует класс X со спецификатором доступа public, то защищенные элемен­ты класса X становятся защищенными элементами класса Y. Это означает, что они могут далее наследоваться классом Z, и эта программа будет откомпилирована и выполнена корректно. Од­нако, если изменить статус X при объявлении Y на private, то, как доказано в следующей про­грамме, класс Z не имеет права доступа к i и j и к функциям get_ij() и put_ij(), поскольку они стали частными членами Y:

МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ

Когда ваш класс наследует характеристики нескольких классов, вы используете множественное наследование. Как вы узнаете из данного урока, C++ полностью поддерживает множественное наследование. К концу этого урока вы изучите следующие основные концепции:

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

Множественное наследование является мощным инструментом объектно-ориентированного программирования.






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.