Конструкторы с параметрами при наследовании

Пусть класс Х задает переменную х, класс Y задает переменную y, класс Z содержит переменную z, которая будет содержать произведение x*y. Каждый из классов содержит функцию void show(void). Используем функции, имеющие одинаковые имена в базовом и производном классах. Все эти функции доступны, так как у них различны полные имена: соответственно Z::show(), X::show() и Y::show().

На этом примере можно экспериментировать, изменяя режим доступа кпеременным x и y и режим доступа при наследовании классов.

#include <iostreame.h> nasl2.cpp

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: public X

{ protected: int y;

public:

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

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

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

int get_y(void) { return y; }

void show(void);

};

Class Z: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<<"\n"; }

Y::Y(int i,int j):X(i)

{// Конструктор класса Y передает значение

y = j; // конструктору класса X, поэтому он должен

cout<<"Конструктор Y\n"; //иметь два параметра

}

Y::~Y(void)

{ cout<<"Деструктор Y\n"; }

void Y::show(void)

{ cout<<"y="<<y<<"\n"; }

Z::Z(int i,int j):Y(i,j) // Конструктор класса Z передает значения

{ cout<<"Конструктор Z\n";} // своих параметров конструктору Y.

Z::~Z(void)

{ cout<<"Деструктор Z\n"; }

void Z::make_z(void)

{ z=x*y; }

void Z::show(void)

{ cout<<z<<"="<<x<<"*"<<y<<";\n"; }

Void main()

{

Z k(4,3); //Создание и инициализация объекта

k.make_z();

k.show();

k.X::show(); // Обратите внимание на вызов функций

k.Y::show(); // show() из разных классов

k.put_x(5); // Задание новых значений переменных x и y

k.put_y(6);

k.make_z();

k.show();

k.X::show();

k.Y::show();

}

Деструкторы классов введены для иллюстрации их вызовов при насле-довании классов. По этой же причине конструкторы классов выдают сообщения.

Необходимо обратить внимание на передачу параметров конструктора производного класса конструктору базового класса:

Y::Y(int i, int j): X(i) {//...};

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

 
  Конструкторы с параметрами при наследовании - student2.ru

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

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

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

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

class <имя_наследующего_класса>: список_базовых_классов {...};

Список базовых классов содержит перечисленные через запятую базовые классы с соответствующими режимами доступа к каждому из базовых классов, например:

class Z: public U, public V, private W { //... };

Предыдущий пример можно переписать в более естественном для него виде

с множественным наследованием, когда классы X и Y будут равноправны. Такое

наследование будет реализовать следующую схему:

 
  Конструкторы с параметрами при наследовании - student2.ru

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

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

конструктор_производного_класса(список аргументов):

базовый_класс1(список аргументов),

...,

базовый_классN(список аргументов)

{ ...

};

Здесь базовый_класс1 ... базовый_классN - имена конструкторов базовых классов, которые наследуются производным классом. Двоеточие отделяет имя конструктора производного класса от списка аргументов базового класса.

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

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

3.9 Подставляемые функции С++

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

1) с использованием модификатора inline перед объявлением функции

inline int f(void)

{ ¼ }

Модификатор inline должен предшествовать всем другим модификаторам. Он вводится для повышение эффективности работы программы (увеливается скорость выполнения программы, т.к. не тратится время на вызов функции, пересылку параметров в стек и возврат значения функции). Однако при этом возрастает размер кода программы, особенно если функция достаточно большая.

Таким образом, лучше использовать inline – функцию, когда функция маленькая. Большие по размеру функции рекомендуется вызывать обычным образом. Пример.

#include <iostream.h>

Class cl

{ int i;

public: int void get_ij(void);

void put_ij (int j);}

inline int cl :: get_i (void)

{return i;}

inline void cl :: put_i (int i)

{ i = j; }

void main (void)

{ cl s;

s.put_i (10);

cout << s.get_i ( );

}

Важно отметить, что для компилятора inline это требование, а не команда создания inline – кода. Inline – код не может быть создан, когда функция содержит циклы, операторы switch, goto или возвращает значение более одного раза. Inline – функции не могут быть рекурсивными или содержать статические (static) переменные.

Функция, имеющая модификатор inline, может быть как функцией - членом какого – либо класса, так и обычной функцией.

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

Пример.

#include <iostream.h>

Class cl

{ int i;

public: int void get_ij(void) {return i;}

void put_ij (int j) {i=j;}

void main (void)

{

cl s;

s.put_i (10);

cout << s.get_i ( );

}

Полиморфизм

Один из путей его реализации - это перегрузка функций. Две или более функции могут иметь одно и тоже имя, но отличаются друг от друга количеством и типом параметров.

Так же, как и другие функции, могут быть перегружены функ­ции-конструкторы. Перегрузка конструкторов класса осуществляется прос­тым объявлением конструкторов с различным набором параметров.

Второй способ реализации полиморфизма - перегрузка операций. Когда перег­ружается знак операции, компилятор анализирует тип операндов и в зависимости от типа делает выбор.

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

Основная форма задания функции-операции, являющейся членом класса:

<тип> <имя_класса> :: operator <знак операции> (список аргументов)

{ операторы, определяющие действия }

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

Пусть программа должна перегружать операции + и = относительно класса vector. Класс vector определяет трехмерный вектор в Евклидовом пространстве. Операция сложения двух векторов выполняется как сложение соответствующих координат.

Пример.

#include <iostream.h> perop.cpp

Class vector

{ int x, y, z; // Заданы три координаты

public:

vector operator+ (vector t);

vector operator= (vector t);

void show(void);

void assign(int mx, int my, int mz);

};

vector vector::operator+ (vector t) // Перегрузка операции +

{ vector temp;

temp.x = x + t.x; // аналогична temp.x = this->x + t.x;

temp.y = y + t.y;

temp.z = z + t.z;

return temp;

}

vector vector::operator= (vector t) // Перегрузка операции =

{ x = t.x;

y = t.y;

z = t.z;

return *this; // Использовали this-указателя на объект класса

}

vector::show(void) // вывод

{ cout << x << ", ";

cout << y << ", ";

cout << z << "\n ";

}

Наши рекомендации