Динамические структуры данных гл



Скачать 163.59 Kb.
страница1/2
Дата28.07.2018
Размер163.59 Kb.
ТипЛекция
  1   2

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

В принципе, механизм структур позволяет решить задачу объединения данных и функций, которые их обрабатывают, в одном объекте – ведь в структуры можно включать такие объекты, как указатели на функции. Указатель на функцию - это переменная, которая содержит адрес функции определённого типа с определённым набором параметров. Соответственно, косвенное обращение по этому указателю представляет собой вызов функции. А если переставить указатель на другую функцию того же типа с соответствующим списком параметров, то и будет вызвана другая функция. Поведение программы становится гибким, а значит, возможности моделирования возрастают.

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

#include

#define M_PI 3.1415926 /* вспомогательные */

#define EPS 1e-9 /* константы */

using namespace std;
typedef double (*function) (double);

//указатель на функцию вида double имя(double)


struct tab { //структура-табулятор функций

double x1, x2; //пределы табулирования

int n; //количество шагов от x1 до x2

function f; //конкретная функция, которую табулируем,

//заданная указателем на неё

static void run(tab t); //функция, которая табулирует f(x)

};

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



void run(tab t) { //функция табулирования любой функции

double dx = (t.x2 - t.x1) / t.n;

cout << endl; cout.width(10); cout << "X";

cout.width(10); cout << "Y";

for (double x = t.x1; x <= t.x2 + EPS; x += dx) {

cout << endl;

cout.width(10); cout << x;

cout.width(10); cout << t.f(x);

}

}

Допишем программу, построив таблицы значений нескольких функций:



//конкретные функции для примера

double f1(double x) { return sin(x); }

double f2(double x) { return cos(x); }

double f3(double x) { return x*x; }


int main() {

tab fun1 = { 0, M_PI, 10, f1 }; run(fun1);

//описали и табулировали одну функцию

tab fun2 = { 0, M_PI/2, 10, f2 }; run(fun2);

//а теперь совсем другую

fun1.f = f3; run(fun1);

//программно поменяли функцию в структуре, переставив

//указатель на другой объект

cin.sync(); cin.get(); return 0;

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



  • в функцию run приходится передавать структуру или указатель на неё;

  • по-прежнему функция run никак не связана со структурой;

  • нельзя разделить или ограничить доступ к переменным и функциям структуры.

Можно сделать функцию "полноценным" членом структуры, но тогда всё равно придётся использовать инструментарий классов, немного забегая вперёд (показаны только изменённые участки кода):
struct tab {

double x1, x2;

int n;

function f;



void run(); //изменено

};
//...


void tab::run(void) { //изменено

double dx = (this->x2 - this->x1) / this->n;

cout << endl; cout.width(10); cout << "X";

cout.width(10); cout << "Y";

for (double x = this->x1; x <= this->x2 + EPS; x += dx) {

cout << endl;

cout.width(10); cout << x;

cout.width(10); cout << this->f(x);

}

}
//...


int main() {

//изменено

tab fun1 = { 0, M_PI, 10, f1 }; fun1.run();

tab fun2 = { 0, M_PI / 2, 10, f2 }; fun2.run();

fun1.f = f3; fun1.run();

cin.sync(); cin.get(); return 0;

}
Примечания:


  1. В этом коде this - "указатель объекта на самого себя", this->x1 означает, что надо взять переменную x1, описанную именно внутри текущей структуры (в fun1 при вызове fun1.run();), а не в другой структуре или глобально.

  2. Оператор :: имеет в C++ наивысший приоритет и означает указание области видимости объекта; в нашем случае то, что функция run относится к структурному типу tab.

В таком коде видно, что функция относится именно к структуре, мы вызываем её в виде fun1.run(), а не run(fun1). Кроме того, мы сэкономили память стека, не передавая целые структуры внутрь функций.

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

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

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

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

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



Класс - абстрактный тип данных, определяющий интерфейс и реализацию для всех своих экземпляров.

Например, объектом (экземпляром) класса "Студент" является конкретный "студент Иванов":

Student Ivanov;

//или чаще:

Student *Ivanov = new Student ();

Объекты обладают тремя базовыми свойствами (они же – три базовых принципа ООП):



  1. Инкапсуляция - механизм языка, разделяющий и ограничивающий доступ к составляющим объект компонентам (методам и свойствам). Например, приватные свойства и методы класса доступны только для экземпляров этого же, но не других классов.

Так, для класса "Служащий" свойство "Зарплата" может быть приватно. А для начисления зарплаты или получения сведений о ней мы можем предусмотреть в классе публичные методы "ПолучитьВеличинуЗП", "НачислитьЗП" и т.п. Вообще, чаще всего свойства класса приватны, а основные методы публичны. Это связано ещё и с тем, что прямое изменение свойств класса в виде объект.свойство=значение не даёт возможности выполнения дополнительных действий и затрудняет модификацию программы.




Поделитесь с Вашими друзьями:
  1   2


База данных защищена авторским правом ©znate.ru 2017
обратиться к администрации

    Главная страница