Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы ТП.doc
Скачиваний:
0
Добавлен:
21.02.2020
Размер:
436.74 Кб
Скачать
  1. Понятие класса и метода. Инкапсуляция.

Класс – это сложная структура данных, определяемая пользователем. В языке С++ под классом понимают производный тип данных, объединенный с множеством функций и операций, работающих с объектами этого типа, и сформированный при помощи описателей class, struct или union. Синтаксис определения класса представляется так:

<описатель> имя_класса [: базовый список] {<список элементов>}

В этом определении <описатель> - это один из описателей class, struct или union Имя_класса – идентификатор из латинских букв и цифр, начинающихся с буквы. В базовом списке перечисляются базовый класс или классы, свойства которых исследуются, а <список элементов> объявляет элементы класса – элементы данных и функции (методы класса).

Инкапсуляция данных – это логическое связывание данных с конкретной операцией.

Инкапсуляция автоматически подразумевает защиту данных.

Для этого в структуре class используется спецификатор раздела private, содержащий данные и методы, доступные только для самого класса.

Если данные и методы содержатся в разделе public, они доступны извне класса. Раздел protected содержит данные и методы, доступные из класса и любого его производного класса. Наличие последних позволяет говорить об иерархии классов, где есть классы - родители - шаблоны для создания классов - потомков. Объекты, полученные из описания класса, называют экземплярами этого класса.

Принадлежащие классу функции называют МЕТОДАМИ класса (компоненты функции).

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

Базовое понятие ООП - инкапсуляция. Подразумевает объединение вместе данных и методов по обработке этих данных внутри классов.

Отличие методов от подпрограмм состоит в том, что Метод описывается внутри класса, может наследоваться, может быть виртуальным, а так же синтаксис:

#include <iostream.h> #include <string.h> class Book { public: Book(char *title) { strcpy(Book::title,title);}; void show book(void) { cout " title; ); private: char title[64] ; }; class Reader { public: Reader(char *name) { strcpy(Reader::name, name);}; void show reader(class Book book) .{ cout <<"Читатель: "<< name<<" "<<"Книга: "; book.show book () ; private: char name[64] ; }; void main(void) { Reader reader("Kris Jamsa") ; Book favorite book("Compiler Internals"); reader.show reader(favorite book);

Reader использовал метод show book объекта Book для вывода заголовка книги. Единственный способ для объекта Reader получить доступ к приватным переменным объекта типа Book, был реализован посредством метода show book.

  1. Объявление классов и создание объектов.

Создание классов:

Класс объявляется с помощью ключевого слова class, за которым указывается имя класса. Затем в фигурных скобках перечисляются члены класса. Синтаксис объявления класса похож на синтаксис объявления структуры. Рассмотрим пример объявления.

Пример 1.

#include <iostream.h>

class myclass // класс под именем myclass

{ int a; // закрытая переменная

public: // ключевое слово (для открытых членов)

void set_a(int n); // функции – открытые члены класса -

int get_a(); // для доступа к закрытой переменной

};

В данном примере объявлен класс myclass, который содержит одну закрытую переменную и две функции, которые являются открытыми членами класса. Перед закрытой переменной ключевое слово private может не ставиться. Оно предполагается по умолчанию. Перед открытыми членами записывается ключевое слово public с двоеточием. В конце описания класса, после закрывающейся фигурной скобки, ставится точка с запятой.

Функции set_a() и get_a() внутри класса только объявлены, но еще не определены (не описаны). Эти функции, являясь открытыми членами класса, должны обеспечивать доступ к закрытым членам. Доступ к закрытой переменной предполагает две операции:

  • присваивание переменной определенного значения;

  • получение значения переменной.

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

Void myclass::set_a(int n)

{ a=n; }

Int myclass::get_a()

{ return a; }

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

Функция set_a() предназначена для присваивания целочисленных значений закрытой переменной а. Поэтому она имеет один параметр, через который и передаются эти значения; функция, в свою очередь, ничего не возвращает. Вторая функция - get_a() – служит для получения значения закрытой переменной. Поэтому она имеет возвращаемое значение, но при её вызове аргументы ей не передаются.

Описание функций – членов можно производить и внутри класса. Тогда они называются встраиваемыми. Такой способ удобен, когда функции не объёмные и за счёт этого не затеняют структуру класса. Ниже приведён пример описания того же класса, но уже со встраиваемыми функциями.

class myclass

{

int a;

public:

void set_a(int n) {a=n;}

Int get_a() {return a;}

};

Как можно видеть, запись получилась более компактной.

Объявление класса myclass не задает ни одного объекта типа myclass. Оно определяет только тип объекта (шаблон). Чтобы создать объект, необходимо указать имя класса - как спецификатор типа данных, после которого записать имя объекта. Например, в следующей строке заданы два объекта типа myclass .

myclass obj1, obj2;

Создание объекта связано с выделением под него памяти. При объявлении же класса память не выделяется.

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

obj1.set_a(10); // обращение к функции set_a( ) первого объекта

obj2.set_a(22); // обращение к функции set_a( ) второго объекта

cout<<obj1.get_a( )<<’\t’; //обращение к функции get_a( ) первого объекта

cout<<obj2.get_a( )<<’\n;//обращение к функции get_a( ) второго объекта

Функции set_a( ) первого объекта в качестве аргумента передаётся численное значение 10, которое присваивается переменной а этого объекта. Аналогично, переменной а второго объекта присваивается значение 22. Каждый объект имеет свою переменную а, свою функцию set_a( ) и функцию get_a( ). Поэтому объекты еще называют экземплярами классов. В третьей строке фрагмента на экран выводится значение функции get_a( ) первого объекта, а в четвертой – второго объекта. После выполнения указанных операций на экране будет: 10 22.

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

Пример с массивом объектов.

#include<iostream.h>

class one // имя класса

{ int a; // закрытая переменная

public:

void set_a(int n) {a=n;} // функция доступа к закрытой переменной

int get_a( ) {return a;} // функция доступа к закрытой переменной

};

void main( ) // главная функция

{

one obj[4]; // создание массива из 4-х объектов

int i;

for(i=0; i<4; i++) // инициализация переменных объектов

obj[i].set_a(i);

for(i=0;i<4;i++) // вывод значений переменных объектов

cout<<obj[i].get_a()<<' ';

}

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

  1. Доступ к членам классов.

Существует три вида спецификаторов, public, private и protected.

Если перед свойством не стоит спецификатор, то по умолчанию (при наследовании классов) он является как private, что означает скрытый.

Если же наследуется не класс, а структура struct, то свойство является общедоступным public.

С двумя спецификаторами мы уже кратко познакомились, теперь давайте полностью ознакомимся со всеми тремя:

  • private — закрытый, то есть к нему можно обращаться только в текущем классе.

  • protected — защищенный, разрешено обращаться как с текущего класса так и с классов наследников.

  • public — общедоступный, разрешено обращаться из любого места программы.

  1. Конструкторы. Назначение и технология использования.

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

Поскольку такая функция конструирует значения данного типа, она называется конструктором. Конструктор распознается по тому, что имеет то же имя, что и сам класс.

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

Например:

class date {

date(int, int, int);

};

Когда класс имеет конструктор, все объекты этого класса будут инициализироваться. Если для конструктора нужны параметры, они должны даваться:

date today = date(23,6,1983);

date xmas(25,12,0); // сокращенная форма (xmas - рождество)

date my_burthday; // недопустимо, опущена инициализация

Часто необходимо обеспечить несколько способов инициализации объекта класса. Это можно сделать, задав несколько конструкторов.

Конструктор без параметров (по умолчанию).

Один из способов сократить число родственных функций - использовать параметры по умолчанию.

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

· конструктор имеет то же имя, что и класс, в котором он объявляется;

· конструктор не возвращает значения даже типа void;

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

· конструктор может иметь аргументы, заданные по умолчанию;

· конструктор - это функция, но он не может быть виртуальным, его нельзя объявить виртуальным;

· невозможно получить в программе адрес конструктора (указатель на конструктор);

· Если конструктор не задан в программе, то он будет автоматически сгенерирован компилятором для построения соответствующих объектов. Все конструкторы сгенерированные компилятором, имеют атрибут public;

· Конструктор по умолчанию для класса Х - это конструктор, который может быть вызван без аргументов;

· Конструктор вызывается автоматически только при описании объекта;

· Объект, содержащий конструктор, нельзя включить в виде компонента в объединение (union);

· Конструктор класса Х не может иметь аргумент типа Х;

· Конструктор, заданный в виде Х::X(const X &), называется конструктором для копирования (copy constructor) класса Х;

  1. Деструкторы. Назначение и использование.

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

имя_класса

У деструктора не может быть параметров (даже типа void), и деструктор не имеет возможности возвращать какой-либо результат, даже типа void. Статус доступа деструктора по умолчанию public (т.е. деструктор доступен во всей области действия определения класса). В несложных классах деструктор обычно определяется по умолчанию.

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

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

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

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

· Деструктор имеет то же имя, что и класс, в котором он объявляется, с префиксом ~(тильдой);

· Деструктор не возвращает значения даже типа void;

· Деструктор не наследуется в производных классах;

· Производный класс может вызвать деструкторы для его базовых классов;

· Деструктор не имеет параметров (аргументов);

· Класс может иметь только один деструктор;

· Деструктор - это функция, и он может быть виртуальным, его можно объявить виртуальным;

· невозможно получить в программе адрес деструктора (указатель на деструктор);

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

· Деструктор вызывается автоматически при разрушении объекта;

· Когда вызывается библиотечная функция exit, вызываются деструкторы только для глобальных объектов;

· Когда вызывается библиотечная функция abort, никакие деструкторы не вызываются.

  1. Передача объектов функциям.

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

При создании копии объекта для передачи ее в функцию конструктор объекта не вызывается. Однако, когда копия объекта внутри функции уничтожается, деструктор вызывается.

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

  1. Дружественные функции.

Функция, не являющаяся членом класса, может иметь доступ к его частным членам в случае, если она объявлена другом (friend) класса. Например, в следующем примере функция frd() объявлена другом класса cl: class cl { ... public: friend void frd(); ... };

Как можно видеть, ключевое слово friend предшествует объявлению функции.

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

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

  1. Иерархия классов.

Иерархия классов возникает в программе за счет механизма наследования, реализованного в Си++.

Наследование – это процесс, посредством которого один объект может приобретать свойства другого. Так как объекты принадлежат к классам, то между классами в этом случае устанавливаются определенные взаимоотношения. Класс, который делегирует свойства, называется базовым (base), а который наследует свойства – производным (derived). У одного базового класса может быть несколько производных, и наоборот, один производный класс может наследовать характеристики нескольких базовых классов.

Схемные примеры наследования:

а) В1 б) В1 В2 в) В1

Д1 Д1

Д

Д2

В примере (а) показана простейшая схема подчиненности с одним базовым (В1) и одним производным (Д1) классом. В примере (б) показан один производный (Д) класс от двух базовых (В1 и В2). На схеме (в) показано, что В1 есть базовый класс для Д1, который в свою очередь является базовым для Д2. В этом последнем случае В1 называется для Д2 косвенно-базовым. Взаимосвязи классов в программе образуют иерархию классов.

При указании наследования соблюдаются следующие правила.

Базовый класс описывается обычным образом. Например:

class base

{

//содержание класса base

};

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

class derived: public base

{

//содержание класса derived

};

Спецификатор выбирается в зависимости от того, как предполагается использовать члены класса base.

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

class derived: public В1, public В2

{

//содержание класса derived

};

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

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

  1. повторное использование данных и функций, ранее созданных в базовом классе, без их дублирования в производном классе;

  2. получение более компактной и обозримой программы;

  3. повышение степени защищенности закрытых членов базового класса;

4) адекватность программной модели предметам реального мира.

  1. Наследование. Технология доступа к членам классов.

Базовый класс является первым в иерархии, он «не знает» сколько у него будет производных классов (или не будет вообще). Поэтому 1) он проектируется самодостаточным в плане доступа к закрытым членам и 2) чле-ны производных классов в нем не упоминаются.

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

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

1.«Круг общения» членов производного класса расширяется только за счет publiс-членов базового класса. Это означает, что члены производного класса могут обращаться напрямую только к открытым членам класса base; private-члены базового класса можно использовать в описании функций производного класса только через открытые члены base class.

2

private

private

.Если наследование производится со спецификатором доступа public, то открытые члены base становятся public-членами класса derived; если наследование производится по типу private, то public-члены базового класса становятся private-членами derived. Private-члены базового класса безусловно наследуются производным классом, но только обращаться к ним члены производного класса derived могут исключительно посредством public-членов base.

#include <iostream.h>

class base

{ int x;

public:

void set_x (int n) {x=n;}

void show_x ( ) {cout<<x<<’\n’;}

};

class derived: public base

{ int y;

public:

void set_y (int k) {y=k;}

void show_y ( ) {cout<<y<<’\n’;}

};

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]