Конструкторы с параметрами при наследовании
Пусть класс Х задает переменную х, класс 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 без каких - либо других изменений, вызовет ошибки при компиляции. В примере использована следующая схема наследования:
При этом классы X и Y по своей сути равноправны, но из-за реализации простого наследования класс Y существенно отличается от класса X, хотя бы числом параметров конструктора.
Избавиться от неравноправия классов можно, используя механизм множественного наследования.
Множественное наследование
В рассмотренных выше примерах каждый производный класс содержал один базовый. Язык С++ позволяет наследование не только от одного, а одновременно от нескольких классов. Форма наследования в этом случае следующая:
class <имя_наследующего_класса>: список_базовых_классов {...};
Список базовых классов содержит перечисленные через запятую базовые классы с соответствующими режимами доступа к каждому из базовых классов, например:
class Z: public U, public V, private W { //... };
Предыдущий пример можно переписать в более естественном для него виде
с множественным наследованием, когда классы X и Y будут равноправны. Такое
наследование будет реализовать следующую схему:
Использование конструкторов, печатающих сообщения, позволяет убедиться в том, что базовые классы создаются в том порядке, в котором они перечислены в списке базовых классов при объявлении производного класса 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 ";
}