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


Полезное:

Как сделать разговор полезным и приятным Как сделать объемную звезду своими руками Как сделать то, что делать не хочется? Как сделать погремушку Как сделать так чтобы женщины сами знакомились с вами Как сделать идею коммерческой Как сделать хорошую растяжку ног? Как сделать наш разум здоровым? Как сделать, чтобы люди обманывали меньше Вопрос 4. Как сделать так, чтобы вас уважали и ценили? Как сделать лучше себе и другим людям Как сделать свидание интересным?


Категории:

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






Наследование и доступность





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

Производный класс имеет доступ ко всем собственным членам, а также к членам базового класса из частей public и protected, но не имеет доступа к членам из части private. Члены класса с доступом protected видимы в пределах класса и в любом классе, непосредственно порожденном из данного.

Ни внешний код клиента, ни функции-члены класса ExtTime не могут обращаться к закрытым переменным базового класса напрямую. Если бы производный класс мог иметь доступ к закрытым членам своего базового класса, то любой программист мог бы сначала создать класс-наследник другого класса, а затем писать код, который будет напрямую просматривать и изменять закрытые данные, а это аннулирует преимущества инкапсуляции.

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

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

Пример.

class book

{сhar title[20];

protected:

char language[10];

int price, year;

public:

void set_data(char *t, char *1, int p, int y)

{strcpу(title, t);

strcpy(language, l);

price=p;

year=y;

}

}; //book

class Guide: public book {

char subject[20];

public:

int change(int x)

{cout<<"Тема"<<subiect;

cout<<"Язык"<<language;

cout<<"Название"<<title; // Ошибка

return(price + x*priсe);

}

}; //Guide

Виды порождения классов.

В иерархии классов соглашение относительно доступности компонентов класса следующее:

private член класса может использоваться только функциями -членами и функциями - "друзьями'' данного класса. В производном классе он недоступен.

protected - то же, что и private, но дополнительно член класса с данным атрибутом доступа может использоваться функциями-членами и функциями - "друзьями" классов, производных отданного.

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

Вид порождения Доступ в базовом классе Доступ в порожденном классе
public public protected private public protected private
protected public protected private protected protected private
private public protected private private private private

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

Пример.

class B1 {...};

class B2 {...};

class B3 {...};

class B4: public B1, protected B2, private B3

{...};

Стандартные преобразования при порождении.

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

- объект порожденного класса неявно преобразуется к объекту базового класса;

- ссылка на порожденный класс неявно преобразуемся к ссылке на базовый класс;

- указатель на порожденный класс неявно преобразуется к указателю на базовый класс.

Пример.

Любое руководство - книга, но книга не всегда руководство.

Пример.

Рассмотрим иерархию порождений:

class Device {...};

class Display: public Device {...};

class Keyboard: public Device {...};

class Mouse: public Device {...};

Определим класс Event, не имеющий отношения к данной иерархии:

class Event {...};

Пусть есть внешняя функция:

extern void f(device &);

тогда

Device dev;

Display disp;

Keyboard keyb;

Mouse ms;

Event ev;

f(dev); // правильно

f(ms); // правильно, т.к. ссылка на Mouse,

f(keyb); // Keyboard, Display неявно

f(disp); // преобразуется к ссылке на Device

f(ev); // Ошибка: для класса Event класс Device

// не является базовым, поэтому

// преобразования не происходит.

Инициализация объекта порожденного класса.

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

Пример.

Пусть в базовом классе book будем инициализировать только название книги и год издания.

class book

{char title[20];

char language[10];

int price;

int year;

public:

book(char *t, int у);

...

}; //book

book:: book(char *t, int у)

{strcpy(title, t);

year=y;

}; // конструктор book

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

Пример.

class Guide: public book

{int level;

char *subject;

public:

Guide(char *book_t, int book_y, char *sub);

// *book_t, book_y - параметры для конструктора

// базового типа, *sub - собственные параметры

}; //Guide

Guide:: Guide(char *book_t, int book_y, char

*sub): bооk(book_t, book_y)

{subject=sub;

} //конструктор Guide

Инициализация объекта производного класса происходит в следующем порядке:

- выполняется конструктор базового класса;

- если производный класс содержит объекты других классов, то инициализируются они;

- выполняется конструктор порожденного класса.

Таким образом, объект производного класса содержит в качестве подобъекта объект базового класса.

Уничтожаются объекты в обратном порядке: сначала производный, потом его компоненты-объекты, а потом базовый объект.

Пример.

Пусть имеется базовый класс Time:

class Time

{int hrs, mins, secs;

public:

void Set(int hours, int minutes, int seconds);

void Increment();

void Write() const;

Time(int initHrs, int initMins, int initSecs);

// Kонструктор

Time();

//Конструктор по умолчанию, ставит время в 0:0:0

}; //Time

enum ZoneType {EST, GST, MST, PST, EDT, GDT, MDT,

PDT};

class ExtTime: public Time

{// Порождаем новый класс ExtTime

ZoneType zone;

public:

void Set(int hours, int minutes, int seсоnds,

ZoneType timeZone);

void Write() const;

ExtTime(int initHrs, initMins, int initSecs,

ZoneType initZone);

// Конструктор

ExtTime();

//Конструктор по умолчанию, время 0:0:0 EST

};

ExtTime:: ExtTime(int initHrs, int initMins, int

initSecs, ZoneType initZone): Time(initHrs,

initMins, initSecs) // Конструктор

{zone=initZone;

}

ExtTime::ExtTime() // Конструктор по умолчанию

{zone = EST;

}

vоid ExtTime:: Set(int hours, int minutes, int

seconds, ZoneType timeZone)

{Time:: Set(hours, minutes, seconds);

zone=timeZone;

}

void ExtTime:: Write() const

{static char zoneString[8][4]={"EST","CST","MST",

"EDT", "CDT", "MDT", "PDT"

};

Time:: Write();

сout<<”Zone”<<zoneString[zone];

}

В первом конструкторе обратите внимание на синтаксис передачи параметров конструктору базового класса, после списка параметров конструктора ExtTime (но перед его телом), нужно вставить то, что называется инициализатором конструктора - двоеточие и имя базового класса с фактическими параметрами для его конструктора. Когда с помощью следующего объявления создается объект класса ExtTime,

ExtTime time1(8, 35, 0, PST);

конструктор этого класса получает четыре параметра. Первые три параметра средствами инициализатора просто передаются конструктору класса Time.

После того, как выполнится конструктор класса Time (создав подобъект базового класса), выполняется тело конструктора класса ExtTime, устанавливая переменную zone равной четвертому параметру.

Второму конструктору (конструктору по умолчанию) инициализатор не требуется, так как конструктору по умолчанию из базового класса не нужны параметры.

Когда объект класса ExtTime создается с помощью объявления

ExtTime time2;

конструктор по умолчанию класса ExtTime сначала неявно вызывает конструктор по умолчанию класса Time, только после чего выполняется собственно его тело, в котором переменной zone присваивается значение EST.

Теперь рассмотрим функцию Set. Эта функция заменяет функцию Set, унаследованную от базового класса. Таким образом, в результате существует две разные функции Set: одна является общедоступным членом класса Time, а другая - общедоступным членом класса ExtTime. Их полными именами будут соответственно Time::Set и ExtTime::Set. Выполнение функции ExtTime::Set начинается с обращения к базовому классу и вызова Time::Set для установки часов, минут и секунд. (производный от Time класс не может напрямую обращаться к закрытым данным hrs, mins, secs, так как эти переменные являются закрытыми членами класса Time). Затем функция присваивает значение переменной zone (закрытому члену класса ExtTime) и заканчивает работу.

Функция Write использует аналогичную стратегию. Она «проникает» в базовый класс и вызывает функцию Time::Write для вывода часов, минут и секунд, затем выводится строка, соответствующая установленному часовому поясу. (Напомним, что значение перечисляемого типа не может прямо выводиться в C++, если бы мы выводили непосредственно значение zone, то получили бы на экране целое число от 0 до 7 - внутренне представление значений типа ZoneType. Функция Write создает массив из восьми строк и выбирает подходящую строку, используя переменную zone в качестве индекса.)

 

Композиция

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

- они абсолютно независимы,

- они связаны наследованием,

- они связаны композицией.

Композиция (или внедрение) - это такое соотношение, когда внутренние данные одного класса А включают объект другого класса Б. Другими словами, объект класса Б содержится внутри объекта класса А.

В C++ нет (и не требуется) специального языкового соглашения для использования композиции. Объект одного класса объявляется просто как один из членов другого класса.

Пример.

Вы разрабатываете программу управления выплатой зарплаты на фабрике. Каждый рабочий имеет временную карточку с его именем. Для регистрации времени рабочий «компостирует» свою карточку: он вставляет ее в специальное устройство, которое прописывает на ней текущее время. Перед тем, как пойти домой, рабочий берет новую карточку и отмечает на ней время окончания работы. Вы решили создать АТД TimeCard для представления карточки каждого рабочего. Абстрактные данные состоят из идентификационного номера и времени. Абстрактные операции включают в себя такие как «Регистрация времени», «Вывод времени», конструкторы и т.д. Чтобы реализовать АТД, Вам нужно выбрать конкретное представление абстрактных данных и написать код для операций. Предполагая, что номер рабочего - это большое целое число, для его представления вы выберете тип long. Размышляя о представлении времени вы вспоминаете, что один ваш товарищ уже писал и отлаживал класс Time (мы используем класс из предыдущего подраздела). Поэтому можно, не задумываясь, объявлять класс TimeCard следующим образом:

#include <time.h>

...

class TimeCard

{long id;

Time timeStamp;

public:

void Punch(int hours, int minutes, int seconds);

void Print() const;

...

TimeCard(long idNum, int initHrs, int init Mins,

int inintSecs);

TimeCard();

};

При разработке класса TimeCard использовалась композиция: объект класса TimeCard состоит из объекта класса Time и переменной типа long. В результате композиции создается отношение «содержит» - объект класса TimeCard содержит объект класса Time в качестве подобъекта.

Реализация класса TimeCard.

Закрытые (private) данные класса TimeCard состоят из переменной (типа long) с именем id и объекта класса Time с именем timeStamp. Функции-члены класса TimeCard могут манипулировать переменной id, используя обычные встроенные операции языка C++, но управлять объектом timeStamp они могут только с помощью функций-членов класса Time. Например, можно реализовать функции Print и Punch следующим образом:

void TimeCard:: Print() const

{cout<<"ID:"<<id<<"\tTime:";

timeStamp Write();

}

void TimeCard:: Punch(int hours, int minutes,

int seconds)

{timeStamp Set(hours, minutes, seconds);

}

Реализацию конструкторов класса описать немного сложнее. Начнем с реализации первого конструктора из объявления класса TimeCard:

TimeCard(long idNum, int initHrs, int initMins,

int initSecs): timestamp(initHrs, initMins,

initSecs) // Инициализатор конструктора

{id=idNum;

}

Инициализатор конструктора, стоящий между списком формальных параметров и телом конструктора используется для того, чтобы передать некоторые из параметров конструктору объекта-члена (timeStamp). Как в случае наследования, так и в случае композиции задача инициализатора конструктора состоит в передаче параметров другому конструктору. Единственное различие в том, что при наследовании перед списком фактических параметров необходимо указать имя базового класса:

Пример.

ExtTime:: ExtTime(int initHrs, int initMins, int

initSecs, ZoneType initZone):Time(initHrs,

initMins, initSecs)

В то время, как при композиции перед списком фактических параметров указывается имя объекта-члена:

TimeCard:: TimeCard(long idNum, int initHrs, int

initMins, int initSecs):timestamp(initHrs,

initMins, initSecs)

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

Пример.

SomeClass:: SomeClass(...): memberObject1

(param1, param2), memberObject2(param3)

 

Date: 2016-05-13; view: 343; Нарушение авторских прав; Помощь в написании работы --> СЮДА...



mydocx.ru - 2015-2024 year. (0.007 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию