Доступ к компонентам структурированного объекта 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Доступ к компонентам структурированного объекта



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

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

В языке Си++ инкапсуляция поддерживается с помощью трёх спецификаторов доступа (ключевых слов) private (частный), protected (защищённый), public (общедоступный). В определении класса компонентные данные и компонентные функции, стоящие после любого из ключевых слов и до конца определения класса или до другого спецификатора доступа считаются частными, защищёнными или общедоступными соответственно. Если при определении класса используется ключевое слово struct, то по умолчанию считается, что компонентные данные являются общедоступными, если class, то частными. Это принятое по умолчанию положение можно изменить явно указав спецификатор доступа. Например:

class F { //поскольку использовано ключевое слово class, то по умолчанию все компонентные //данные и функции размещённые в определении c начала определения и до нового //спецификатора доступа, а если он отсутствует, то до конца определения класса будут //частными (private)

компонентные данные; //private данные

компонентные функции; //private компонентные функции

public: //C этого точки определения и до следующего спецификатора доступа или, если он //отсутствует, до конца определения класса все определённые компонентные данные и //компонентные функции будут считаться общедоступными

компонентные данные; //public данные

компонентные функции; //public компонентные функции

};

Перепишем класс комплексное число с учётом принципа инкапсуляции. Определим все его компонентные данные как частные (private), а компонентные функции как общедоступные (public).

class comp { //Скрытая (частная) часть класса comp

double Re, Im;

static int count;

public: //Интерфейсная (общедоступная) часть класса comp

comp (){Re= 0; Im= 0; count++;} //Компонентная функция (конструктора по умолчанию)

comp(double r, double i) {Re = r; Im = i; count++;} //Конструктор

comp(comp &T) { Re= T.Re; Im = T.Im; count++;} // Конструктор копии

comp(double r) {Re = r; Im=0; count++;} // Конструктор преобразования типа

~comp(){count--;} // Деструктор

void display(){cout<<“\n Re =”<<Re<<”\t Im =“<<Im;}//Компонентная функции display()

static int GetCountComp(){return count;}

void SetRe(double r){Re = r;}

void SetIm(double i){ Im = i;}

double GetRe(){return Re;}

double GetIm(){return Im;}

friend comp raz(comp& A, comp& B);

};

int comp::count = 0; //Инициализация статического компонентного данного

Поскольку теперь компонентные данные Re, Im и count класса сomp являются частными, то к ним с помощью операции точка или стрелочка теперь можно обратится только в теле компонентных функций класса comp. Например, в конструкторе копии мы видим такое обращение Re= T.Re; Во всех других функциях, в том числе и main(), эта строка выдавала бы на этапе компиляции сообщение об ошибке. Поэтому, если все таки возникает необходимость изменять или получать значения реальной и мнимой части объекта комплексное число вне тел компонентных функций класса comp, то необходимо написать несколько интерфейсных функций SetRe(), SetIm(), GetRe(), GetIm(), которые и решают эту задачу. Причём поскольку тела этих функций очень маленькие, то мы делаем их online (подставляемыми), поэтому вызов такой функции не сильно отличается от прямого обращения к полю данных структурированного объекта, следовательно, почти не замедляет скорость работы программы.

Перепишем определение функции sum () с учётом того, что доступ к полям Re и Im теперь можно получить только через вызов компонентных функций SetRe(), SetIm(), GetRe(), GetIm().

Определение обычной (не компонентной) функции sum()

comp sum(comp A, comp& С){

comp D;

D.SetRe(A.GetRe() +С.GetRe());

D.SetIm (A.GetIm() +С.GetIm());

return D;

}

Как видно из определения функции sum (), оно стало несколько громоздким. Этого можно избежать воспользовавшись ещё одной возможностью, предоставляемой языком Си++. Она состоит в том, что можно объявить функцию, дружественной к классу. В этом случае дружественная функция будет иметь право доступа к любым данным класса в не зависимости от их спецификатора доступа. Для того чтобы сделать функцию дружественной необходимо, чтобы она была описана в определении класса со спецификатором friend, как показано выше в определении класса на примере функции raz().

Определение обычной (не компонентной) функции raz()

comp raz(comp& A, comp& С){

comp D;

D.Re = A.Re -С.Re;

D.Im = A.Im -С.Im;

return D;

}

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

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

Итак, дружественная функция:

• не может быть компонентной функцией того класса, по отношению к которому определяется как дружественная;

• может быть глобальной функцией (как в предыдущей программе):

class CL { friend int f1 (...);... }; int fl(...) { тело_функции }

• может быть компонентной функцией другого ранее определенного класса:

class CL1 {... char f2 (...);... };

class CL2 {... friend char CL1::f2(...);... };

В примере класс CL1 c помощью своей компонентной функции f2 () получает доступ к компонентам класса CL2. Компонентная функция некоторого класса (CL1) может быть объявлена дружественной функцией другому классу (CL2), если только определение этого первого класса размещено раньше, чем определение второго (CL).

• может быть дружественной по отношению к нескольким классам:

// Предварительное неполное определение класса class CL2;

class CL1 { friend void ff{CLl,CL2);... };

class CL2 { friend void ff{CLl,Cl2);... };

void ff(CL1 cl,CL2 c2) { тело_функции }

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

class X2 {

friend class X1;

};

class X1 {... // Определение дружественного класса

void f1(...);

void f2(...);

};

 

В данном примере функции f1 и f2 из класса X1 являются друзьями класса Х2, хотя они описываются без спецификатора friend.

Все компоненты класса X2 доступны в дружественном классе X1. Дружественный класс может быть определен позже (ниже), нежели описан как дружественный.

 

Классы и шаблоны

Шаблоны, которые иногда называют родовыми или параметризованными типами, позволяют создавать (конструировать) семейства родственных функций и классов.

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

template <список_параметров_шаблона> определекие_функции

Здесь угловые скобки являются неотъемлемым элементом определения. Список параметров шаблона должен быть заключен именно в угловые скобки.

Аналогично определяется шаблон семейства классов:

template <список_параметров_шаблона> определение класса

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

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

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

Следующий шаблон позволяет автоматически формировать классы векторов с указанными свойствами:

template <class T> // Т - параметр шаблона

class Vector {

Т *data; // Начало одномерного массива

int size; // Количество элементов в массиве

public:

Vector(int); // Конструктор класса vector

~Vector() { delete[] data; } // Деструктор

// Расширение действия (перегрузка) операции "[]":

Т& operator[] (int i) { return data[i]; }

};

// Внешнее определение конструктора класса:

template <class T>

Vector <T>::Vector(int n) { data = new T[n]; size = n; };

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

имя_параиетризованного_класса<фактические_параметры_шаблона> имя_объекта (паракетры_конструктора);

В нашем случае определить вектор, имеющий восемь вещественных координат типа double, можно следующим образом:

Vector <double> Z(8); Проиллюстрируем сказанное следующей программой:

 

#include "templatevec.h" // Шаблон классов "вектор"

#include <iostream.h>

main(){ // Создаем объект класса "целочисленный вектор":

Vector <int> Х(5);

// Создаем объект класса "символьный вектор":

Vector <char> C(5);

// Определяем компоненты векторов:

for (int i = 0; i < 5; i++) { X[i] = i; C[i] = 'A' + i;}

for (i - 0; i < 5; i++) cout << " " << X[i] << ' ' << C[i]; }

Результат выполнения программы:

0 A 1 B 2 С 3 D 4 E

В программе шаблон семейства классов с общим именем vector используется для формирования двух классов с массивами целого и символьного типов. В соответствии с требованием синтаксиса имя параметризованного класса, определенное в шаблоне (в примере Vector), используется в программе только с последующим конкретным фактическим параметром (аргументом), заключенным в угловые скобки. Параметром может быть имя стандартного или определенного пользователем типа. В данном примере использованы стандартные типы int и char. Использовать имя Vector без указания фактического параметра шаблона нельзя - никакое умалчиваемое значение при этом не предусматривается.

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

#include <iostream.h>

template <class T, int size = 64>

class row {

T *data;

int length;

public: row() { length = size; data = new T[size]; }

~row() { deleted data; }

T& operator [] (int i) { return data[i]; }

};

void main () {

row <float,8> rf;

row <int,8> ri;

for (int i = 0; i < 8; i++) { rf[i] = i; ri[i] = i * i; }

for (i = 0; i < 8; i++) cout<<«" " << rf[i] << ' ' << ri[i];

}

Результат выполнения программы:

0 0 1 1 2 4 3 9 4 16 5 25 6 36 7 49

В качестве аргумента, заменяющего при обращении к шаблону параметр size, взята константа. В общем случае может быть использовано константное выражение, однако выражения, содержащие переменные, использовать в качестве фактических параметров шаблонов нельзя.

 

Проектные задания

1. Определите новый тип данных, описывающий товар в магазине.

2. Перечислите какие операции определены над структурированными объектами по умолчанию.

3. Назовите какие операции определены над линейным списком.

4. Перечислите чем структура отличается от объединения.

5. Определите с помощью оператора define синоним для имени структурированного типа.

6. Напишите в произвольном порядке фамилии учеников вашей группы и составте на основании этого списка бинарное дерево.

7. Набирите и отладте пример реализации типа «комплексное число».

8. Перепишите класс комплексное число, перегрузив операции +, ‑

9. Перепишите класс комплексное число, сделав его параметризированным

10.

 

Тесты рубежного контроля

1. Как с помощью имени стркутурированного объекта получить доступ к его полям?

a) С помощью операции точка

b) С помоью операции запятая

c) С помощью операции стрелочка.

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

a) С помощью операции стрелочка.

b) С помощью операции запятая

c) С помощью операции присваивание.

3. С помощью каких ключевых слов прораммист может определить структурированный объект?

a) struct

b) typedef

c) define

4. Линейный список, операции вставки и удаления элементов которого происходит на разных концах списка называется:

a) Дек

b) Очередь

c) Стек

5. Линейный список, операции вставки и удаления элементов которого происходит только на одном конце списка называется:

a) Дек

b) Очередь

c) Стек

6. Как в структурированном объекте определяется статический компонент?

a) С помощью ключевого слова extern

b) С помощью ключевого слова auto

c) С помощью ключевого слова static

7. Что такое this?

a) Указатель на структурированный объект.

b) Ключевое слово

8. Когда вызывается конструктор по умолчанию?

a) При создании структурированных объектов без параметров

b) При создании структурированных объектов с параметрами

c) При копировании структурированных объектов по значению

d) При преобразовании типа структурированного объекта

9. Когда вызывается конструктор для структурированного объекта?

a) При создании структурированных объектов без параметров

b) При создании структурированных объектов с параметрами

c) При копировании структурированных объектов по значению

d) При преобразовании типа структурированного объекта

10. Когда вызывается конструктор копии для структурированного объекта?

a) При создании структурированных объектов без параметров

b) При создании структурированных объектов с параметрами

c) При копировании структурированных объектов по значению

d) При преобразовании типа структурированного объекта

11. Когда вызывается конструктор преобразования типа для структурированного объекта?

a) При создании структурированных объектов без параметров

b) При создании структурированных объектов с параметрами

c) При копировании структурированных объектов по значению

d) При преобразовании типа структурированного объекта

12. В какую компонентную функцию при её вызове скрыто не передаётся указатель this?

a) В конструктор

b) В деструктор

c) В статическую компонентную функцию

13. Какая из перечисленных операций не может быть перегружена?

a) +

b) ‑

c) #

14. Можно ли менять арность операции?

a) Да

b) Нет

c) Можно, но при некоторых условиях

15. Можно ли менять ранг операции?

a) Да

b) Нет

c) Можно, но при некоторых условиях

16. Что означает спецификатор доступа public?

a) общедоступный

b) частный

c) защещённый

17. Что означает спецификатор доступа protected?

a) общедоступный

b) частный

c) защещённый

18. Что означает спецификатор доступа private?

a) общедоступный

b) частный

c) защещённый

19. С помощью какого ключевого слова создаются параметризированные классы

a) Template

b) Private

c) Typedef

20. 18 Можно ли не определять в классе конструктор копии?

a) Да

b) Нет

c) Да, но только в определённых случаях.

Таблица правильных ответов

№ Вопроса Правильный ответ № Вопроса Правильный ответ
  A   D
  A   C
  A   C
  B   B
  C   B
  C   A
  A   C
  A   B
  B   A
  C   C

 

Квалиметрическая оценка

В зависимости от времени и цели проведения тестирования количество вопросов в тесте может менятся. Поэтому, чтобы можно было оценить успеваемость тестируемых студентов, предлагается следующая шкала. Если количество правильных ответов лежит в диапазоне

от 85 до 100% - это отлично;

от 70 до 84% - это хорошо;

от 55 до 69% - это удовлетворительно;

менее 55% - это не удовлетворительно.

 

 

Список ЛИТЕРАТУРЫ

1. В.В. Подбельский Язык Си++: Учебное пособие. - 5-е изд. - М.: Финансы и статистика, 2000. - 560с. ил.

2. Ален И. Голуб. Си и Си++ правила программирования. - М.: БИНОМ, 1996. - 272с.

3. Бьерн Страуструп Язык программирования Си++. 3-е изд. /Пер. с англ. - СПб.; М.: «Невский диалект» - «Издательство БИНОМ», 1999. - 991с. ил.

4. Дональд Э. Кнут Искусство программирования. 3-е изд.: Пер. с англ.: Уч.пос. - М.: Издательский дом "Вильяс", 2000. - с., ил.

5. Влах И., Сингхал К. Машинные методы анализа и проектирования электронных схем: Пер. с англ. – М.: Радио и связь, 1988. – 560 с. ил.

 

 

ПРИЛОЖЕНИЕ 1

Таблицы кодов ASCII

Таблица П1.1

Коды управляющих символов (0 - 31)

Символ Код 10 Код 08 Код 16 Клавиши Значение
nul       ^@ Нуль
soh       ^A Начало заголовка
stx       ^B Начало текста
etx       ^C Конец текста
eot       ^D Конец передачи
enq       ^E Запрос
ack       ^F Подтверждение
bel       ^G Сигнал (звонок)
bs       ^H Забой (шаг назад)
ht       ^I Горизонтальная табуляция
lf     ^J Перевод строки
vt     0B ^K Вертикальная табуляция
ff     ^L Новая страница
cr     0D ^M Возврат каретки
so     ^N Выключить сдвиг
si     0F ^O Включить сдвиг
dle       ^P Ключ связи данных
dc1       ^Q Управление устройством 1
dc2       ^R Управление устройством 2
dc3       ^S Управление устройством 3
dc4       ^T Управление устройством 4
nak       ^U Отрицательное подтверждение
syn       ^V Синхронизация
etb       ^W Конец передаваемого блока
can       ^X Отказ
em       ^Y Конец среды
sub     ^Z Замена
esc     1B ^ [ Ключ
fs     ^ \ Разделитель файлов
gs     1D ^ ] Разделитель группы
rs     1E ^ ^ Разделитель записей
us     1F ^_ Разделитель модулей

В графе "клавиши" обозначение ^ соответствует нажатию клавиши Ctrl, вместе с которой нажимается соответствующая "буквенная" клавиша, формирующая код символа.

Таблица П 1.2

Символы с кодами 32 – 127

Символ Код 10 Код 08 Код 16   Символ Код 10 Код 08 Код 16
пробел         =     3D
!         >     ЗЕ
"         ?     3F
#         @      
$         А      
%         В      
£         С      
'         D      
(         Е      
)         F      
*       G      
+       Н      
,     2C   I      
-     2D   J     4A
.       K   113. 4B
/     2F   L     4C
          М     4D
          N     4E
          О     4F
          Р      
          Q      
          R      
          S      
          Т      
          U      
          V      
:     ЗА   W      
;     ЗВ   X      
<     ЗС   Y      
Z     5A   m     6D
[     5B   n     6E
\     5C   о     6F
]     5D   p      
^     5E   q      
_     5F   г      
`         s      
a         t      
b         u      
c         v      
d         w      
e         x      
f         Y      
g         z    
h         {    
i         |    
j     6A   }     7D
k     6B   ~    
      6C   del     7F

 


Таблица П1.3

Символы с кодами 128 - 255 (Кодовая таблица 866 - MS-DOS)

 

Символ Код 10 Код 08 Код 16   Символ Код 10 Код 08 Код 16
А         Ь    
Б         Э     9D
В         Ю    
Г         Я     9F
Д         а     АО
Е         б     А1
Ж         в     А2
З         г     A3
И         д     А4
Й         е     А5
К       ж     А6
Л       з     А7
М       и     А8
Н     8D   й     А9
О       к     АА
П     8F   л     АВ
Р         м     АС
С         н     AD
Т         о     АЕ
У         п     AF
Ф             ВО
X             В1
Ц             В2
Ч         |     ВЗ
Ш             В4
Щ             В5
Ъ           В6
Ы           В7
    В8       D6
    В9       D7
    ВА       D8
    ВВ   ┘       D9
    ВС       DA
    BD       DB
    BE       DC
    BF       DD
    СО       DE
    С1       DF
    С2   Р     ЕО
    СЗ   С     Е1
    С4   Т     Е2
+     С5   У     ЕЗ
    С6   ф     Е4
    С7   X     Е5
    С8   Ц     Е6
    С9   Ч     Е7
    СА   Ш     Е8
    СВ   Щ     Е9
    СС   Ъ     ЕА
    CD   : Ы     ЕВ
    СЕ   Ь     ЕС
    CF   Э     ED
    DO   Ю     ЕЕ
    D1   Я     EF
    D2   ≡ илиЁ     F0
    D3   ± или ё     F1
    D4       F2
    D5       F3
    F4   .     FA
    F5       FB
÷     F6   n     FC
    F7   z     FD
·     F8       FE
    F9         FF

 

 

Таблица П1.4

Символы с кодами 128 - 255 (Кодовая таблица 1251 – MS Windows)



Поделиться:


Последнее изменение этой страницы: 2016-08-10; просмотров: 163; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.135.198.49 (0.139 с.)