Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Constructor Coord Constructor Dot Destructor Dot Destructor Coord

Class Basel

{ <элементы_класса> }; class Base2

{ <элементы_класса> }; class Derived: <спецификатор_доступа> Basel, <спецификатор_доступа> Base2

// Базовый класс
// Производный класс

// Базовый класс

<спецификатор_доступа> BaseN { <элементы_класса> };

Здесь <спецификатор_доступа> - это public, protected или private; он не является обязательным и по умолчанию принимает значение private для классов и public для структур. Спецификатор доступа при наследовании определяет уровень доступа к элементам базового класса, который получают элементы производного класса. В приведенной ниже таблице описаны возможные варианты наследуемого доступа. Доступ в производном классе

Доступ в базовом классе



 

  public protected private
public public protected недоступны
protected protected protected недоступны
private private private недоступны

Изучение таблицы показывает, что спецификатор наследуемого доступа устанавливает тот уровень доступа, до которого понижается уровень доступа к членам, установленный в базовом классе. Из этого правила можно сделать исключения. Если спецификатор наследуемого доступа установлен как private, то public -члены базового класса будут являться private -членами в производном классе. Однако можно сде­лать некоторые из членов базового класса открытыми в производном классе, объявив их в секции public производного класса.

 



Объектно-ориентированное программирование


Лекция 5


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



Таким образом, при таком наследовании открытые и защищенные члены базового класса будут дос­тупны только для членов данного производного класса, и все члены базового класса, кроме явно объяв­ленных в разделе public или protected, будут закрытыми для следующих производных классов. Этот прием позволяет отсечь доступ к членам базового класса при построении иерархии классов с отношением родитель - потомок.

Напомним, что при любом способе наследования в производном классе доступны только открытые (public) и защищенные (protected) члены базового класса (хотя наследуются все члены базового клас­са). Иначе говоря, закрытые члены базового класса остаются закрытыми, независимо от того, как этот класс наследуется.

Производный класс наследует не все члены базового класса. Перечислим ненаследуемые члены класса:

> конструкторы;

> конструкторы копирования;

> деструкторы;

> перегруженные операторы присвоения;

> друзья класса.

Работа конструктора и деструктора

Чтобы более наглядно изучить работу конструкторов и деструкторов базового и производного клас­сов, рассмотрим следующий пример:

// базовый класс // конструктор базового класса // деструктор базового класса // производный класс – наследник класса Coord // конструктор производного класса // деструктор производного класса // создание объекта производного класса

Class Coord

{

public:

Coord () { cout «"Constructor Coord \n"; }

-Coord () { cout «"Destructor Coord \n"; } }

class Dot: public Coord { public:

Dot () { cout «"Constructor Dot \n"; }

-Dot () { cout «"Destructor Dot \n"; } }

void main () {

Dot A; }

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

Constructor Coord Constructor Dot Destructor Dot Destructor Coord

При создании экземпляра производного класса автоматически вызывается конструктор базового класса. После того как объект создан, конструктор базового класса становится недоступным. Хотя конст­руктор базового класса не наследуется, он вызывается только компилятором при конструировании объект производного класса. Конструктор базового класса вызвать явно нельзя.

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

Базовый класс координат

В предыдущей лекции мы создали два класса: класс точки Dot и класс вектора Vec. У этих классов существуют идентичные данные-члены х и у, идентичные функции-члены GetX () и GetY (), а также по­хожие функции-члены Print () и Input (). В данной задаче целесообразно создать абстрактный базовый класс Coord, который будет содержать общие черты поведения класса Dot и класса Vec. include <iostream.h> include <windows.h> include <math.h>



Объектно-ориентированное программирование


Лекция 5


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



class Coord // базовый класс координат

{

protected: // защищённые члены класса, доступны только классам-потомкам

double x, y; // координаты

double Hypotenuse () { return sqrt (x*x + y*y); } // возвращает расстояние между

public: // текущей точкой и началом координат

// конструкторы класса, потомками не наследуются

Coord () { x = 0; y = 0; } // инициализирует координаты нулями

Coord (double X, double Y) { x = X; y = Y; } // инициализирует координаты заданными значениями

// функции-члены класса, наследуются потомками

void Print (); // выводит на экран значения координат

void Input (); // вводит значения координат с клавиатуры

inline double GetX () const { return x; } // возвращает координату x

inline double GetY () const { return y; } // возвращает координату y

inline void SetX (double X) { x = X; } // передаёт заданное значение в координату x

inline void SetY (double Y) { y = Y; } // передаёт заданное значение в координату y

};

// функция-член класса Coord позволяет выводить значения координат на экран

void Coord:: Print () {

cout <<"\tx="<<x<<"\ty="<<y<<’\n’; // выводит на экран значения координат с подписями

}

// функция-член класса Coord позволяет вводить значения координат с помощью клавиатуры

void Coord:: Input () {

cout <<"\tx="; cin>>x; // выводит на экран приглашение “x=” и вводит значения x с клавиатуры cout <<"\ty="; cin>>y; // выводит на экран приглашение “y=” и вводит значения y с клавиатуры }

Базовый класс Coord содержит данные-члены x, y и два конструктора. Конструктор класса Coord() параметров не имеет и инициализирует данные-члены нулями. Конструктор класса Coord(double X, double Y) позволяет инициализировать данные-члены заданными значениями.

Функция-член класса Print() позволяет выводить на экран значения данных-членов в виде:

x = 5 y = 6 Функция-член класса Input() позволяет выводить на экран приглашение и вводить значения данных-членов с клавиатуры в виде:

x = 5

y = 6 Функции-члены класса GetX() и GetY() позволяет возвращать в место вызова значения данных-членов x и y соответственно. Эти функции пригодятся нам в том случае, когда нам потребуются значения закрытых (private) данных-членов за пределами класса, в котором они объявлены.

Производный класс точки

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

#include ”Coord.h”

class Dot: public Coord // производный класс Dot наследует элементы базового класса Coord

{

char name; // имя точки

public:

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

Dot (char Name): Coord () { name = Name; }

Dot (char Name, double X, double Y): Coord (X, Y) { name = Name; }

// переопределённые функции-члены

void Input (); // выводит приглашение и вводит координаты текущей точки с клавиатуры

void Print (); // выводит на экран имя и координаты текущей точки

double Dist (Dot A); // возвращает расстояние между текущей и заданной точками

};

// член класса Dot выводит на экран заголовок, имя точки и значения координат текущей точки

Объектно-ориентированное программирование

Void Dot:: Print ()


Лекция 5


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



{

char S [ ] = ”Координаты точки ”; // объявляет и инициализирует строку заголовка

CharToOem (S, S); // преобразует символы строки в кириллицу

cout«S«name«":”; // выводит на экран заголовок и имя точки

Coord:: Print (); // выводит значения координат на экран

}

// член класса Dot выводит на экран приглашение, имя точки текущей точки и // вводит значения её координат с клавиатуры

Void Dot:: Input ()

{

char S [ ] =”Введите координаты точки ”; // объявляет и инициализирует строку приглашения
CharToOem (S, S); // преобразует символы строки в кириллицу

cout«S«name«":”; // выводит на экран приглашение и имя точки

Coord:: Input (); // вводит значений координат точки с клавиатуры

}

// член класса Dot возвращает расстояние между текущей и заданной точками

double Dot:: Dist (Dot A) {

Dot T (’T’, A.x - x, A.y - у); // объявляет объект класса Dot и инициализирует его данные-члены

// значениями катетов прямоугольного треугольника
return T.Hypotenuse (); // вызывает унаследованную функцию-член класса Dot

}

Производный класс Dot унаследовал у своего предка Coord данные-члены х и у, а также функции-члены Print (), Input (), GetX (), GetY (), SetX (), SetY () и Module (). К членам класса Coord он до­бавляет данное-член name, два конструктора и открытую функцию Dist (). Кроме того, класс Dot переоп­ределяет функции-члены Print () и Input ().

Данные-члены х и у объявлены в базовом классе Coord как защищённые (protected), поэтому они доступны в производном классе Dot. Например, функция Dist () член класса Dot содержит обращение к свойству текущего объекта - х, а к свойству параметра функции А с помощью оператора “точка” - А.х. Если данные-члены х и у объявить в базовом классе Coord как закрытые (private), они будут недоступ­ны в производном классе Dot. В этом случае получить значение свойства х можно будет только с помо­щью функция GetX (), которая унаследована от базового класса.

Поскольку классы Dot и Vec имеют общий базовый класс Coord, то у нас появилась возможность не дублировать алгоритм (теорема Пифагора) двух близких задач - вычисление расстояния между двумя точками и нахождения длины вектора. Для этого мы сделали следующие шаги:

> функцию вычисления длины гипотенузы Hypotenuse () мы объявили в классе Coord как защищён­ную. Потомки её унаследуют, но она будет закрытой;

> в классе Dot мы объявили функцию Dist (Dot A), которая при вычислении расстояния между теку­щей точкой и заданной использует унаследованную функцию Hypotenuse ();

> в классе Vec мы объявили функцию Module (), которая при вычислении длины вектора использует унаследованную функцию Hypotenuse ().

Производный класс вектора

Для решения нашей задачи нам также необходим класс вектора. Вектор характеризуется своими про­екциями на оси координат и именем. Несмотря на различный математический смысл данных, нам удобно унаследовать данные-члены х и у у класса Coord. Имя точки и имя вектора имеют одинаковый смысл, но точку мы привыкли обозначать одной буквой, а вектор - двумя. Для хранения этих данных нам требуются разные типы данных - символ и строка. Поэтому имя объекта не является в нашей задаче общей характе­ристикой, которую можно ввести в абстрактный класс. Таким образом, базовый класс Coord мы будем ис­пользовать дважды в качестве родительского класса, что должно уменьшить размер программы.

Интерфейс класса вектора имеет вид:

Файл Vech include ”Doth”

class Vec: public Coord // класс вектора

{

char name[3]; // имя вектора

public:

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



Объектно-ориентированное программирование


Лекция 5


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



Vec (char* pName): Coord () { strncpy (name, pName, 3); }

Vec (char* pName, double X, double Y): Coord (X, Y) { strncpy (name, pName, 3); } Vec (char* pName, Dot A, Dot B); // конструирование вектора по координатам его концов

// переопределённые функции-члены

void Print (); // выводит на экран имя и проекции вектора

void Input (); // вводит проекции вектора с клавиатуры

double Modul () { return Hypotenuse (); } // возвращает длину вектора

};

Файл Vec.cpp

#include ”Vec.h”

// конструирование вектора по координатам его концов

Vec:: Vec (char* pName, Dot A, Dot B): Coord (B.GetX () – A.GetX (), B.GetY () - A.GetY ()) {

strncpy (name, pName, 3); }

// функция-член класса Vec выводит на экран заголовок, имя вектора и значения проекций вектора

Void Vec:: Print ()

{

char S [ ] = "Проекции вектора "; // объявляет и инициализирует строку заголовка
CharToOem (S, S); // преобразует символы строки в кириллицу

cout<<S<<name<<":"; // выводит на экран заголовок и имя вектора

Coord:: Print (); // выводит значения проекций вектора на экран

}

// функция-член класса Vec выводит на экран приглашение, имя вектора и // позволяет вводить с клавиатуры значения проекций вектора

void Vec:: Input () {

char S [ ] = "Введите проекции вектора "; // объявляет и инициализирует строку приглашения
CharToOem (S, S); // преобразует символы строки в кириллицу

cout<<S<<name<<'\n'; // выводит на экран приглашение и имя вектора

Coord:: Input (); // вводит значения проекций вектора с клавиатуры

}

Интерфейс класса Vec аналогичен, в основном, интерфейсу класса Dot. Обратим внимание на третий конструктор Vec (char*N, Dot A, Dot B), который получает в качестве параметров имя вектора и две точки – начало и конец вектора. Описание конструктора достаточно громоздко и поэтому мы расположили его не в интерфейсе класса, а в файле Vec.cpp.

Данный конструктор вызывает конструктор базового класса Coord (double X, double Y) и передаёт ему вычисленные проекции вектора (проекции вектора = координаты конца – координаты начала). Для по­лучения координат точки используются функции GetX () и GetY () – члены класса Dot, которые он унас­ледовал от базового класса Coord. Тело конструктора использует значение первого параметра для ини­циализации имени объекта.

Файл Main.cpp

#include ”Vec.h”

Void main()

{

Dot C ('C', 3, 4), D ('D'); // объявляет точки

C.Print (); D.Print (); // выводит на экран координаты точек

cout<<"CD="<<C.Dist (D)<<'\n'; // выводит на экран расстояние между точками

Vec CD ("CD", C, D); // конструирует вектор по координатам его концов

CD.Print (); // выводит на экран проекции вектора

}
Эта программа выводит на экран следующее: Координаты точки C: x = 3 y = 4 Координаты точки D: x = 0 y = 0 CD = 5 Проекции вектора CD: x = -3 y = -4 |CD| = 5

сout << "|DC|="<<DC.Modul () << '\n'; // выводит на экран длину вектора



 

 

Объектно-ориентированное программирование


Лекция 5


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



Использование конструктора с параметрами

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

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

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

<констр_базового_класса> (<список_аргументов>) { <тело_конструктора> }

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

Первый конструктор класса Dot имеет вид:

Dot (char Name): Coord () { name = Name; }

При вызове этого конструктора сначала вызывается конструктор базового класса Coord (), который инициализирует унаследованные данные-члены x и y нулями. Затем в теле конструктора данное-член name инициализируется значением параметра конструктора Name.

Второй конструктор класса Dot имеет вид:

Dot (char Name, double X, double Y): Coord (X, Y) { name = Name; }

При вызове второго конструктора сначала вызывается конструктор базового класса Coord (double X, double Y), который инициализирует унаследованные данные-члены x и y значениями параметров конст­руктора X и Y. Затем в теле конструктора данное-член name инициализируется значением параметра конструктора Name.

Переопределение и вызов членов базового класса

В производном классе обычно добавляются новые члены к членам базового класса. Однако сущест­вует также возможность переопределения (или замещения) членов базового класса. Обычно используется переопределение функций-членов базового класса. Чтобы переопределить функцию-член базового клас­са в производном классе, достаточно включить ее прототип в объявление этого класса и затем дать ее определение. Прототипы переопределяемой функции в базовом и производном классах должны совпа­дать.

Иногда возникает необходимость вызвать функцию-член базового класса, а не ее переопределенный вариант, это можно сделать с помощью операции разрешения области видимости, применяемой в форме:

<имя_класса>:: <имя_члена>

Это дает возможность компилятору "видеть" за пределами текущей области видимости. Особенно часто это приходится делать при переопределении функций-членов. Переопределяемая функция-член может вызывать соответствующую функцию-член базового класса, а затем выполнять некоторую допол­нительную работу (или наоборот).

В приведённом выше классе Dot была переопределена функция Print (). Тело функции содержит операторы, которые выводят на экран заголовок “Координаты точки ” и имя точки. Далее вызывается функция Print (), которая является членом родительского класса Coord, с помощью оператора: Coord:: Print ();

Член родительского класса Coord функция Print () выводит на экран в той же строке координаты точки. В результате на экране появится, например, следующий текст:

Координаты точки A: x = 4 y = 5



Объектно-ориентированное программирование

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...