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



ЗНАЕТЕ ЛИ ВЫ?

Правила наследования различных методов

Поиск

Конструкторы не наследуются, поэтому производный класс должен иметь собственный конструктор.

Порядок вызова конструктора в производном классе осуществляется по следующим правилам:

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

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

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

4. Не наследуются операция присваивания, следовательно, её требуется явно определить в производном классе

Вызов функции базового класса предполагает копирование фрагментов кода из функций базового класса в функции производного. При этом осуществляется сокращение объектного кода и достигается упрощение модификации программы.

Правила наследования деструктора:

1. Деструкторы не наследуются. Если программист не описал в производном классе деструктор, он формируется по умолчанию и вызывает деструкторы всех базовых классов

2. В отличии от конструктора при описании деструктора производного класса в нем не требуется явно вызывать деструкторы базовых классов, т.к. это будет сделано автоматически.

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

Доступ к переопределенному методу базового касса выполняется через имя, уточненное с помощью операции доступа к области видимости (::).

Присваивание объектов при наследовании

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

Формат записи операции присваивания выглядит следующим образом:

<объект родительского класса> = <объект производного класса>


Множественное наследование

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

class <имя производного класса>:

<вид наследования> <имя базового класса>

<вид наследования> <имя базового класса>

...

{...};

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

<имя конструктора производного класса> (<список аргументов>),

<имя конструктора базового класса 1> (<список аргументов>),

...

{<тело конструктора производного класса>}

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

- активируются конструкторы для базовых классов (в порядке их перечисления в объявлении производного класса)

- затем активируется конструктор класса.

Пример 1

Class X

{

protected:

int x;

public:

x(int i); /* конструктор с параметром */

~x(void); /* деструктор */

void put_x(int i) {x=i;}

int get_x(void) {return x;}

void show(void);

};

Class Y

{

protected:

int y;

public:

Y(int i); /* конструктор с параметром */

~Y(void); /* деструктор */

void put_y(int i) {y=i;}

int get_y(void) {return y;}

void show(void)

};

Class Z: public X, public Y

{

protected;

int z;

public:

z(int i, int j); /* конструктор с параметром*/

~z(void); /* деструктор */

void make_z(void);

void show(void);

};

X::x(int i)

{

x=i;

cout<<” конструктор X \n”;

}

x::~x(void){ cout<<” деструктор X \n”;}

void x::show(void) { cout<<” X=”<<x<<endl;}

Y::y(int i)

{

y=j;

cout<<” конструктор Y \n”;

}

y::~y(void){ cout<<” деструктор Y \n”;}

void y::show(void){ cout<<” Y=”<<y<<endl;}

/* конструктор класса Z передает значение своих параметров */

z::z(int i, in j):Y(j), X(i) { cout<<” конструктор Z \n”;}

z::~x(void){ cout<<” деструктор Z \n”;}

void Z::make_z(void){ z=x*y;}

void Z::show(void){ cout<<z<<” =”<<x<<”*”<<y<<endl;}

main(void)

{

z zobj(3, 5); /* создание и инициализация объекта*/

zobj.make_z();

zobj.show();

zobj.x::show();

zobj.y::show(); /* осуществление вызова функции из разных классов*/

zobj.put_x(7);/* задание новых значений переменных */

zobj.put_x(9);

zobj.make_z();

zobj.show();

zobj.x::show();

zobj.y::show();

return 0;

}


 

Виртуальное наследование

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

При виртуальном наследовании производный класс описывается так:

сlass <имя производного класса>:

virtual <вид наследования> <имя базового класса>

{...};

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

1. конструктор виртуально наследованного базового класса

2. конструкторы базовых классов, в порядке их перечисления при объявлении

3. конструкторы объектных полей

4. конструктор производного класса

Деструкторы вызываются в обратном порядке.

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

Пример 1

Class fixed

{

protected:

int fix;

public:

fixed(void) /* конструктор без параметров */

{ cout<<” вызов конструктора \n”;}

fixed(int fix)::fix(fix) /* конструктор с параметром */

{ cout<<” вызов конструктора fixed int \n”;}

};

class derived_1: virtual public fixed /* виртуальное наследование */

{

public:

int one;

derived_1(void) { cout<<” Вызов конструктора 1 \n”;};

};

class derived_2: virtual public fixed /* виртуальное наследование */

{

public:

int two;

derived_2(void) { cout<<” Вызов конструктора 2 \n”;};

};

class derived: public derived_1, public derived_2 /* объявление производного класса непрямого потомка */

{

public:

derived(void) {cout<< “ вызов конструктора derived” <<endl;}

derived(int fix);

fixed(fix) { cout<<” вызов конструктора derived int“<<endl;}

void out() { cout<<”Fix = ”<< fix; }

 


 

Глава 7. Полиморфизм

Полиморфизм

Существует 2 вида полиморфизма: простой полиморфизм (статический), базирующийся на механизме раннего связывания, и сложный полиморфизм (динамический), использующий механизм позднего связывания. Простой полиморфизм поддерживается языком C++ на этапе компиляции (раннее связывание) и реализуется с помощью механизма переопределения функции. Эти полиморфные функции в C++ называются переопределенными. В соответствии с общими правилами они могут различаться типом возвращаемого параметра и сигнатурой (количеством и типом передаваемых параметров).

Динамический полиморфизм – выбор функции производится по используемому при выводе имени, типу вызываемого объекта и типу используемый параметров. Если объявленный тип не совпадает с типом использованного объекта, тип этого объекта идентифицируется и проверяется во время исполнения программы, что позволяет осуществить правильный выбор соответствующей функции. Это и называется динамическим полиморфизмом (поздним связыванием). Динамическое связывание приводит к небольшим издержкам памяти и времени выполнения программы, поэтому в C++ его используют только в случае явного указания, посредством ключевого слова virtual. Динамическое связывание используется путем объявления виртуальной функции. Виртуальная функция может реализовываться алгоритмами, которые, как правило, отличаются между собой. Если коды функции различны, то механизм виртуальности не включается. При использовании виртуальной функции, каждый аспект виртуальной функции, вызываемой из базового класса, определяется на этапе выполнения программы, когда известно для какого объекта вызван метод: базового класса или производного. Если вызов функции осуществляется через указатель на базовый класс, нужный аспект функции вызывается в зависимости от того, адрес объекта какого класса будет содержать указатель.

 

Пример 1

Class Window

{

public:

Window(const Point &upper.Dest, const Point &size, const string &title);

~Window();

void move(const Point &new_upper_left);

Point upper_left() const;

Point lower_right() const;

virtual void change_size(const Point fnew_size);

//Если новый размер point меньше минимального, сократить до этого минимального размера

int move cursor(contst Point &where);

Point cursor() const;

Display char character()const;

string Tile() const;

void add(Display char c);

void add(const string &str);

void clear();

void scroll_up();

void scroll_down();

private:

// данные для Window

};

/* Функция collapse, которая использует только общие для всех типов окон функции, что обеспечивает возможность её применения для окон любого типа. */

void collapse(Window &w) 

{

w.clear();

w.change_size(Point(5,5));

//метод изменения размера change.size

}

main(int argc, char *argv[])

{

window w(Point(10,6), Point(30,6), AWIndow);

shell_Window sw(Point(3, [1]), Point(10, 10), "A shell_window")

collapse(w);

collapse(sw);

return 0;

}

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


 

Виртуальные методы

Раннее связывание

Работа с объектами чаще всего производится через указатели. Указателю на базовый класс можно присвоить значение адреса объекта любого производного класса.

Пример 1

// Описывается указатель на базовый класс:

monstr *p:

// указатель ссылается наобъект производного класса:

p = new daemon;

 

Вызов методов объекта происходит в соответствии с типом указателя, а не фактическим типом объекта, на который он ссылается, поэтому при выполнении оператора, например, p->draw(1. 1, 1, 1); будет вызван метод класса monstr, а не класса daemon, поскольку ссылки на методы разрешаются во время компоновки программы. Этот процесс называется ранним связыванием или статическим полиморфизмом. Чтобы вызвать метод класса daemon, можно использовать явное преобразование типа указателя: (daemon * p)->draw(l, 1, 1, 1);

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


Позднее связывание

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

Для определения виртуального метода используется спецификатор virtual.

Пример 2



Поделиться:


Последнее изменение этой страницы: 2021-07-18; просмотров: 174; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

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