Научная Петербургская Академия

Курсовая: Библиотека классов

Курсовая: Библиотека классов

ГУМАНИТАРНЫЙ УНИВЕРСИТЕТ

“Запорожский институт государственного и муниципального управления"

Факультет управления

Кафедра ПРОГРАМмирования и иНФОРМАЦионных ТЕХНОЛОГиЙ

ПОЯСНИТЕЛЬНАЯ ЗАПИСКА

к курсовой роботе по дисциплине "Объектно-ориентированное программирование"

на тему “БИБЛИОТЕКА КЛАССОВ ДЛЯ УЧЕТА МЕБЕЛИ"

Подготовил студент группы ДИ-102 Воронин А.А._______________

подпись

Научный руководитель

Старший преподаватель Слободянюк

М.А.______________

подпись

Запорожье,

2004

ЗАДАНИЕ

Составить класс 'Квартира' и иерархию классов мебели. В классах мебели есть

возможности установки другой мебели сверху. В классе 'Квартира' имеются

список мебели и её количество.

РЕФЕРАТ

Размер пояснительной записки составляет 37 страниц, в которые входит:

титульный лист, задание, реферат, содержание, вступление, основная часть,

вывод, список литературы, приложение №1, приложение №2. Работа была написана

из двух источников, а также основываясь на собственных знаниях.

Пояснительная записка начинается с введения, далее идет основная часть, в

которой рассматриваются некоторые теоретические особенности классов, также в

основной части рассмотрены основные принципы разработки программы из

приложения №1, после следует вывод, в котором очень кратко рассматриваются

принципы представления данных в ООП по средствам классов.

Программа, которую необходимо было написать согласно заданию представлена в

приложении №1, а в приложении №2 представлен пример её выполнения.

Программа была написана в среде разработки Dev-C++.

КЛЮЧЕВЫЕ СЛОВА:

С++, ПРОГРАММИРОВАНИЕ, CLASS, COUT, CIN, STRUCT, PUBLIC, PRIVATE,

КОНСТРУКТОР, ДЕСТРУКТОР, ИНТЕРФЕЙС, ФУНКЦИЯ, ОПЕРАТОР, ПЕРЕМЕННЫЕ.

СОДЕРЖАНИЕ

ВВЕДЕНИЕ.................................................................................................................51.

КЛАССЫ И

ЧЛЕНЫ............................................................................................6

1.1.

Функции-члены........................................................................................6

1.2.

Классы........................................................................................................7

1.3. Ссылка на

себя..........................................................................................8

1.4.

Удаление...................................................................................................11

1.5.

Подстановка............................................................................................12

2. ИНТЕРФЕЙСЫ И

РЕАЛИЗАЦИЯ..................................................................14

2.1. Альтернативные

реализации.........................................................14 3. ДРУЗЬЯ

КЛАССОВ............................................................................................17

3.1. Вложенные

классы..............................................................................19

3.2. Статические

члены.............................................................................21

4. КОНСТРУКТОРЫ И

ДЕСТРУКТОРЫ..........................................................23 4.1.

Локальные

переменные........................................................................23

4.2. Объекты класса как

члены...............................................................24 5.

СОЗДАНИЕ

ПРОГРАММЫ..............................................................................27

ВЫВОД......................................................................................................................30СПИСОК

ЛИТЕРАТУРЫ.....................................................................................31

ПРИЛОЖЕНИЕ

№1................................................................................................32

ПРИЛОЖЕНИЕ

№2................................................................................................37

ВВЕДЕНИЕ

Понятие класса, служит в С++ для того, чтобы дать программисту инструмент

построения новых типов. Ими пользоваться не менее удобно, чем встроенными. В

идеале использование определенного пользователем типа не должно отличаться от

использования встроенных типов. Различия возможны только в способе

построения.

Тип есть вполне конкретное представление некоторого понятия. Например, в С++

тип float с операциями +, -, * и т.д. является хотя и ограниченным, но

конкретным представлением математического понятия вещественного числа. Новый

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

понятия, которое не находит прямого и естественного отражения среди

встроенных типов. Например, в программе из области телефонной связи можно

ввести тип trunk_module (линия-связи), в видеоигре - тип explosion (взрыв), а

в программе, обрабатывающей текст, - тип list_of_paragraphs (список-

параграфов). Обычно проще понимать и изменять программу, в которой типы

хорошо представляют используемые в задаче понятия. Удачно подобранное

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

транслятору обнаруживать недопустимое использование объектов, которое в

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

Главное в определении нового типа - это отделить несущественные детали

реализации от тех его характеристик, которые существенны для правильного его

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

Такое разделение обеспечивается тем, что вся работа со структурой данных и

внутренние, служебные операции над нею доступны только через специальный

интерфейс (через "одно горло").

1. КЛАССЫ И ЧЛЕНЫ

Класс - это пользовательский тип. Этот раздел знакомит с основными средствами

определения класса, создания его объектов, работы с такими объектами и,

наконец, удаления этих объектов после использования.

1.1. Функции-члены

Посмотрим, как можно представить в языке понятие даты, используя для этого

тип структуры и набор функций, работающих с переменными этого типа:

struct date { int month, day, year; };

date today;

void set_date(date*, int, int, int);

void next_date(date*);

void print_date(const date*);

// ...

Никакой явной связи между функциями и структурой date нет. Ее можно

установить, если описать функции как члены структуры:

struct date {

int month, day, year;

void set(int, int, int);

void get(int*, int* int*);

void next();

void print();

};

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

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

запись обращения к члену структуры:

date today;

date my_birthday;

void f()

{

my_birthday.set(30,12,1950);

today.set(18,1,1991);

my_birthday.print();

today.next();

}

Поскольку разные структуры могут иметь функции-члены с одинаковыми именами,

при определении функции-члена нужно указывать имя структуры:

void date::next()

{

if (++day > 28 ) {

// здесь сложный вариант

}

}

В теле функции-члена имена членов можно использовать без указания имени

объекта. В таком случае имя относится к члену того объекта, для которого была

вызвана функция.

1.2. Классы

Мы определили несколько функций для работы со

структурой date, но из ее описания не следует, что это единственные функции,

которые предоставляют доступ к объектам типа date. Можно установить такое

ограничение, описав класс вместо структуры:

class date {

int month, day, year;

public:

void set(int, int, int);

void get(int*, int*, int*);

void next();

void print()

};

Общая часть - представляет собой интерфейс с объектами класса. Поэтому

структура – это такой класс, в котором по определению все члены являются

общими.

void date::print() // печать даты в принятом в

США виде

{

cout << month << '/' << day << '/' << year ;

}

Однако от функций не членов частные члены класса date уже ограждены:

void backdate()

{

today.day--; // ошибка

}

Есть ряд преимуществ в том, что доступ к структуре данных ограничен явно

указанным списком функций. Любая ошибка в дате (например, December, 36, 1985)

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

локализация ошибки – происходит даже до первого пуска программы.

1.3. Ссылка на себя

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

для которого она была вызвана:

class X {

int m;

public:

int readm() { return m; }

};

void f(X aa, X bb)

{

int a = aa.readm();

int b = bb.readm();

// ...

}

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

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

скрытый параметр под именем this. Считается, что в каждой функции-члене

класса X указатель this описан неявно как

X *const this;

и инициализируется, чтобы указывать на объект, для которого функция-член

вызывалась. Этот указатель нельзя изменять, поскольку он постоянный (*const).

Явно описать его тоже нельзя, т.к. this - это служебное слово. Можно дать

эквивалентное описание класса X:

class X {

int m;

public:

int readm() { return this->m; }

};

Для обращения к членам использовать this излишне. В основном this

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

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

вызывается, будет доступен ей только по чтению. Тот факт, что функция не

будет изменять объект, для которого она вызывается (т.е. this*),

обозначается служебным словом const в конце списка параметров:

class X {

int m;

public:

readme() const { return m; }

writeme(int i) { m = i; }

};

Функцию-член со спецификацией const можно вызывать для постоянных объектов, а

функцию-член без такой спецификации - нельзя:

void f(X& mutable, const X& constant)

{

mutable.readme(); // нормально

mutable.writeme(7); // нормально

constant.readme(); // нормально

constant.writeme(7); // ошибка

}

Тип указателя this в постоянной функции-члене класса X есть const X *const.

Это значит, что без явного приведения с помощью this нельзя изменить значение

объекта:

class X {

int m;

public:

// ...

void implicit_cheat() const { m++; } // ошибка

void explicit_cheat() const { ((X*)this)->m++; }

// нормально

};

Отбросить спецификацию const можно потому, что понятие "постоянства" объекта

имеет два значения. Первое, называемое "физическим постоянством" состоит в

том, что объект хранится в защищенной от записи памяти. Второе, называемое

"логическим постоянством" заключается в том, что объект выступает как

постоянный (неизменяемый) по отношению к пользователям. Операция над

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

этом не нарушается его постоянство с точки зрения пользователя. Операциями,

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

значений, ведение статистики, изменение переменных-счетчиков в постоянных

функциях-членах.

1.4. Удаление

Пользовательские типы чаще имеют, чем не имеют, конструкторы, которые

проводят надлежащую инициализацию. Для многих типов требуется и обратная

операция - деструктор, гарантирующая правильное удаление объектов этого типа.

Деструктор класса X обозначается ~X ("дополнение конструктора"). В частности,

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

освобождаемая деструктором. Вот, например, традиционное определение типа

стек, из которого для краткости полностью выброшена обработка ошибок:

class char_stack {

int size;

char* top;

char* s;

public:

char_stack(int sz) { top=s=new char[size=sz]; }

~char_stack() { delete[] s; } // деструктор

void push(char c) { *top++ = c; }

void pop() { return *--top; }

};

Когда объект типа char_stack выходит из текущей области видимости, вызывается

деструктор:

void f()

{

char_stack s1(100);

char_stack s2(200);

s1.push('a');

s2.push(s1.pop());

char ch = s2.pop();

cout << ch << '\n';

}

Когда начинает выполняться f(), вызывается конструктор char_stack, который

размещает массив из 100 символов s1 и массив из 200 символов s2. При возврате

из f() память, которая была занята обоими массивами, будет освобождена.

1.5. Подстановка

Программирование с классами предполагает, что в программе появится множество

маленьких функций. По сути, всюду, где в программе с традиционной

организацией стояло бы обычное обращение к структуре данных, используется

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

транслятором. В результате программа может стать крайне неэффективной. Хотя

вызов функции в C++ и не столь дорогостоящая операция по сравнению с другими

языками, все-таки цена ее много выше, чем у пары обращений к памяти,

составляющих тело тривиальной функции.

Преодолеть эту трудность помогают функции-подстановки (inline). Если в

описании класса функция-член определена, а не только описана, то она

считается подстановкой. Это значит, например, что при трансляции функций,

использующих char_stack из предыдущего примера, не будет использоваться

никаких операций вызова функций, кроме реализации операций вывода! Другими

словами, при разработке такого класса не нужно принимать во внимание затраты

на вызов функций. Любое, даже самое маленькое действие, можно смело

определять как функцию без потери эффективности. Это замечание снимает

наиболее часто приводимый довод в пользу общих членов данных.

Функцию-член можно описать со спецификацией inline и вне описания класса:

class char_stack {

int size;

char* top;

char* s;

public:

char pop();

// ...

};

inline char char_stack::pop()

{

return *--top;

}

Отметим, что недопустимо описывать разные определения функции-члена,

являющейся подстановкой, в различных исходных файлах. Это нарушило бы понятие

о классе как о цельном типе.

2. ИНТЕРФЕЙСЫ И РЕАЛИЗАЦИЯ

Что представляет собой хороший класс? Это нечто, обладающее хорошо

определенным множеством операций. Нечто, рассматриваемое как "черный ящик",

управлять которым можно только посредством этих операций. Нечто, чье

фактическое представление можно изменить любым мыслимым способом, но не

изменяя при этом способа использования операций. Нечто, что может

потребоваться в нескольких экземплярах.

Очевидные примеры хороших классов дают контейнеры разных видов: таблицы,

множества, списки, вектора, словари и т.д. Такой класс имеет операцию

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

занесен в контейнер? Могут быть операции упорядочивания всех членов и

просмотра их в определенном порядке. Наконец, может быть операция удаления

члена. Обычно контейнерные классы имеют конструкторы и деструкторы.

2.1. Альтернативные реализации

Пока описание общей части класса и функций-членов остается неизменным, можно,

не влияя на пользователей класса, менять его реализацию. В подтверждение

этого рассмотрим таблицу имен из программы калькулятора. Структура ее такова:

struct name {

char* string;

name* next;

double value;

};

Задание конструктора со стандартным значением параметра гарантирует, что

старые программы, в которых не использовался размер таблицы, останутся

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

изменить класс, не влияя на программы пользователей класса. Теперь

конструктор и деструктор создают и уничтожают кэшированные таблицы:

table::table(int sz)

{

if (sz < 0) error("размер таблицы отрицателен");

tbl = new name*[size = sz];

for ( int i = 0; i<sz; i++) tbl[i] = 0;

}

table::~table()

{

for (int i = 0; i<size; i++) {

name* nx;

for (name* n = tbl[i]; n; n=nx) {

nx = n->next;

delete n->string;

delete n;

}

}

delete tbl;

}

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

вариант table::~table(). Функция поиска практически совпадает с приведенной в

примере калькулятора:

name* table::look(const char* p, int ins)

{

int ii = 0;

char* pp = p;

while (*pp) ii = ii<<1 ^ *pp++;

if (ii < 0) ii = -ii;

ii %= size;

for (name* n=tbl[ii]; n; n=n->next)

if (strcmp(p,n->string) == 0) return n;

name* nn = new name;

nn->string = new char[strlen(p)+1];

strcpy(nn->string,p);

nn->value = 1;

nn->next = tbl[ii];

tbl[ii] = nn;

return nn;

}

Очевидно, что функции-члены класса должны перетранслироваться всякий раз,

когда в описание класса вносится какое-либо изменение. В идеале такое

изменение никак не должно отражаться на пользователях класса. К сожалению,

обычно бывает не так. Для размещения переменной, имеющей тип класса,

транслятор должен знать размер объекта класса. Если размер объекта изменится,

нужно перетранслировать файлы, в которых использовался класс.

Можно не знать определения всего класса, если представлять каждый объект как

указатель на "настоящий" объект. Это позволяет решить задачу, поскольку все

указатели будут иметь одинаковый размер, а размещение настоящих объектов

будет проводиться только в одном файле, в котором доступны частные части

классов. Однако, такое решение приводит к дополнительному расходу памяти на

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

члена. Еще хуже, что каждый вызов функции с автоматическим объектом класса

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

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

членами класса. Наконец, такое изменение сделает невозможным связывание

программ на С++ и на С, поскольку транслятор С будет по другому обрабатывать

структуры (struct). Поэтому такое решение было сочтено неприемлемым для С++.

3. ДРУЗЬЯ КЛАССОВ

Пусть определены два класса: vector (вектор) и matrix (матрица). Каждый из

них скрывает свое представление, но дает полный набор операций для работы с

объектами его типа. Допустим, надо определить функцию, умножающую матрицу на

вектор. Для простоты предположим, что вектор имеет четыре элемента с

индексами от 0 до 3, а в матрице четыре вектора тоже с индексами от 0 до 3.

Доступ к элементам вектора обеспечивается функцией elem(), и аналогичная

функция есть для матрицы. Можно определить глобальную функцию multiply

(умножить) следующим образом:

vector multiply(const matrix& m, const vector& v);

{

vector r;

for (int i = 0; i<3; i++) { // r[i] = m[i] * v;

r.elem(i) = 0;

for (int j = 0; j<3; j++)

r.elem(i) +=m.elem(i,j) * v.elem(j);

}

return r;

}

Это вполне естественное решение, но оно может оказаться очень неэффективным.

При каждом вызове multiply() функция elem() будет вызываться 4*(1+4*3) раз.

Если в elem() проводится настоящий контроль границ массива, то на такой

контроль будет потрачено значительно больше времени, чем на выполнение самой

функции, и в результате она окажется непригодной для пользователей. С другой

стороны, если elem() есть некий специальный вариант доступа без контроля, то

тем самым мы засоряем интерфейс с вектором и матрицей особой функцией

доступа, которая нужна только для обхода контроля.

Если можно было бы сделать multiply членом обоих классов vector и matrix, мы

могли бы обойтись без контроля индекса при обращении к элементу матрицы, но в

то же время не вводить специальной функции elem(). Однако, функция не может

быть членом двух классов. Функция может стать другом класса, если в его

описании она описана как friend (друг). Например:

class matrix;

class vector {

float v[4];

// ...

friend vector multiply(const matrix&, const vector&);

};

class matrix {

vector v[4];

// ...

friend vector multiply(const matrix&, const vector&);

};

Функция-друг не имеет никаких особенностей, за исключением права доступа к

закрытой части класса. В частности, в такой функции нельзя использовать

указатель this, если только она действительно не является членом класса.

Описание friend является настоящим описанием. Оно вводит имя функции в

область видимости класса, в котором она была описана, и при этом происходят

обычные проверки на наличие других описаний такого же имени в этой области

видимости. Описание friend может находится как в общей, так и в частной

частях класса, это не имеет значения.

Теперь можно написать функцию multiply, используя элементы вектора и матрицы

непосредственно:

vector multiply(const matrix& m, const vector& v)

{

vector r;

for (int i = 0; i<3; i++) { // r[i] = m[i] * v;

r.v[i] = 0;

for ( int j = 0; j<3; j++)

r.v[i] +=m.v[i][j] * v.v[j];

}

return r;

}

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

класса. Для этого есть краткая форма записи:

class x {

friend class y;

// ...

};

В результате такого описания все функции-члены y становятся друзьями класса x.

3.1. Вложенные классы

Описание класса может быть вложенным. Например:

class set {

struct setmem {

int mem;

setmem* next;

setmem(int m, setmem* n) { mem=m; next=n; }

};

setmem* first;

public:

set() { first=0; }

insert(int m) { first = new setmem(m,first); }

// ...

};

Доступность вложенного класса ограничивается областью видимости лексически

объемлющего класса:

setmem m1(1,0); // ошибка: setmem не находится

// в глобальной области видимости

Если только описание вложенного класса не является совсем простым, то лучше

описывать этот класс отдельно, поскольку вложенные описания могут стать очень

запутанными:

class setmem {

friend class set; // доступно только для членов set

int mem;

setmem* next;

setmem(int m, setmem* n) { mem=m; next=n; }

// много других полезных членов

};

class set {

setmem* first;

public:

set() { first=0; }

insert(int m) { first = new setmem(m,first); }

// ...

};

Полезное свойство вложенности - это сокращение числа глобальных имен, а

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

В функции-члене область видимости класса начинается после уточнения X:: и

простирается до конца описания функции. Например:

M1 X::f(M2 a) // ошибка: имя `M1' вне области видимости

{ /* ... */ }

X::M1 X::f(M2 a) // нормально

{ /* ... */ }

X::M1 X::f(X::M2 a) // нормально, но третье уточнение X:: излишне

{ /* ... */ }

3.2. Статические члены

Класс - это тип, а не некоторое данное, и для каждого объекта класса

создается своя копия членов, представляющих данные. Однако, наиболее удачная

реализация некоторых типов требует, чтобы все объекты этого типа имели

некоторые общие данные. Лучше, если эти данные можно описать как часть

класса. Например, в операционных системах или при моделировании управления

задачами часто нужен список задач:

class task {

// ...

static task* chain;

// ...

};

Описав член chain как статический, мы получаем гарантию, что он будет создан

в единственном числе, т.е. не будет создаваться для каждого объекта task. Но

он находится в области видимости класса task, и может быть доступен вне этой

области, если только описан в общей части. В этом случае имя члена должно

уточняться именем класса:

if (task::chain == 0) // какие-то операторы

В функции-члене его можно обозначать просто chain. Использование статических

членов класса может заметно сократить потребность в глобальных переменных.

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

его независимым от отдельных объектов его класса.

Описание статического члена - это только описание, и где-то в программе

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

например, такое:

task* task::task_chain = 0;

void task::shedule(int p) { /* ... */ }

Естественно, что и частные члены могут определяться подобным образом.

Слово static одно из самых перегруженных служебных слов в С и С++. К

статическому члену, представляющему данные, относятся оба основных его

значения: "статически размещаемый", т.е. противоположный объектам,

размещаемым в стеке или свободной памяти, и "статический" в смысле с

ограниченной областью видимости, т.е. противоположный объектам, подлежащим

внешнему связыванию. К функциям-членам относится только второе значение

static.

4. КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ

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

этого класса. Если у класса есть деструктор, он вызывается всякий раз, когда

уничтожается объект этого класса. Объект может создаваться как:

[1] автоматический, который создается каждый раз, когда его описание

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

котором он описан;

[2] статический, который создается один раз при запуске программы и

уничтожается при ее завершении;

[3] объект в свободной памяти, который создается операцией new и уничтожается

операцией delete;

[4] объект-член, который создается в процессе создания другого класса или при

создании массива, элементом которого он является.

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

конструктор или как временный объект. В обоих случаях такой объект не имеет

имени.

4.1. Локальные переменные

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

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

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

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

их создании:

void f(int i)

{

table aa;

table bb;

if (i>0) {

table cc;

// ...

}

// ...

}

Здесь aa и bb создаются (именно в таком порядке) при каждом вызове f(), а

уничтожаются они при возврате из f() в обратном порядке - bb, затем aa. Если

в текущем вызове f() i больше нуля, то cc создается после bb и уничтожается

прежде него.

Поскольку aa и bb - объекты класса table, присваивание aa=bb означает

копирование по членам bb в aa. Такая интерпретация присваивания может

привести к неожиданному (и обычно нежелательному) результату, если

присваиваются объекты класса.

4.2. Объекты класса как члены

Рассмотрим пример:

class classdef {

table members;

int no_of_members;

// ...

classdef(int size);

~classdef();

};

Цель этого определения, очевидно, в том, чтобы classdef содержал член,

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

конструктора table::table() с параметром size. Это можно сделать, например,

так:

classdef::classdef(int size)

:members(size)

{

no_of_members = size;

// ...

}

Параметр для конструктора члена (т.е. для table::table()) указывается в

определении (но не в описании) конструктора класса, содержащего член (т.е. в

определении classdef::classdef()). Конструктор для члена будет вызываться до

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

Списки параметров для членов отделяются друг от друга запятыми (а не

двоеточиями), а список инициализаторов для членов можно задавать в

произвольном порядке.

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

Подобные описания конструкторов существенны для типов, инициализация и

присваивание которых отличны друг от друга, иными словами, для объектов,

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

членов типа ссылки. Однако, как показывает член no_of_members из приведенного

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

типа.

Если конструктору члена не требуется параметров, то и не нужно задавать

никаких списков параметров. Так, поскольку конструктор table::table() был

определен со стандартным значением параметра, равным 15, достаточно такого

определения:

classdef::classdef(int size)

: members(size), no_of_members(size)

{

// ...

}

Тогда размер таблицы friends будет равен 15.

Если уничтожается объект класса, который сам содержит объекты класса

(например, classdef), то вначале выполняется тело деструктора объемлющего

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

Рассмотрим вместо вхождения объектов класса в качестве членов традиционное

альтернативное ему решение: иметь в классе указатели на члены и

инициализировать члены в конструкторе:

class classdef {

table* members;

table* friends;

int no_of_members;

// ...

};

classdef::classdef(int size)

{

members = new table(size);

friends = new table; // используется стандартный

// размер table

no_of_members = size;

// ...

}

Поскольку таблицы создавались с помощью операции new, они должны уничтожаться

операцией delete:

classdef::~classdef()

{

// ...

delete members;

delete friends;

}

Такие отдельно создаваемые объекты могут оказаться полезными, но учтите, что

members и friends указывают на независимые от них объекты, каждый из которых

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

памяти суммарно занимают больше места, чем объект-член.

5. СОЗДАНИЕ ПРОГРАММЫ

Программа начинается помощью директивы #include подключим заголовочный файл

iostream:

#include<iostream>//директива препроцессора

Эта директива указывает препроцессору добавить в программу содержимое файла

iostream. Буквы io в слове iostream означает ввод(input) данных в программу и

вывод(output) данных из программы. Схема ввода/вывода в языке С++ включает в

себя несколько определений, которые находятся в файле iostream.

Если используется iostream, а не iostream.h, то чтобы определения в этом

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

пространства имен:

using namespace std;

Она называется директивой использования(using directive).

Подключаем класс kvartira:

class kvartira{}

Описание класса разбивается на две части, служебное слово public (общий), где

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

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

общедоступного раздела составляет абстрактную часть конструкции, т.е.

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

сохранение целостности данных и называется сокрытием данных.

В общедоступном разделе объявляем конструктор и деструктор:

kvartira(int initialrazmer);

~kvartira();

Конструктор – это метод класса, имя которого совпадает с именем самого

класса, они служат для создания и инициализации объектов класса, а деструктор

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

память. Деструктору всегда присваивается имя класса с символом тильды(~)

вначале. Деструктор не принимает никаких аргументов и не возвращает никаких

значений.

Getrazmer() объявляется, как функция доступа к данным-членам класса, эта

функция возвращает текущее значение переменной класса itsrazmer. А функция

Setrazmer(int razmer) изменяет значение переменной-члена itsrazmer. Также в

общедоступном классе объявляется метод доступа meb().

В частном классе объявляется переменная itsrazmer.

Класс mebel, построен подобным способом, тольво в нем объявлено больше

методов, определяющие мебель в программе.

kvartira::kvartira(int initialrazmer)

{

itsrazmer=initialrazmer;

}

Таким образом записан конструктор класса kvartira, деструктор же в данной

программе не выполняет никаких действий, записывается он так:

kvartira::~kvartira()

{}

Getrazmer – это открытая функция обеспечения доступа, которая в подобной

записи возвращает значение переменной-члена itsrazmer:

int kvartira::Getrazmer()

{

return itsrazmer;

}

Определим еще одну открытую функцию обеспечения доступа Setrazmer, которая

устанавливает переменную-член itsrazmer равной значению, переданному

параметром razmer:

void kvartira::Setrazmer(int razmer)

{

itsrazmer=razmer;

}

Определим метод meb, который возвращает void и не имеет параметров, в данном

случае используется для вывода на экран текста "komnata"

void kvartira::meb()

{

cout<<"komnata\n";

}

Определение методов и функций класса mebel аналогично.

Функция main() подобно всем другим функциям должна объявить тип возвращаемого

значения, в данном случае она возвращает значение типа int, а это означает,

что по окончании работы эта функция возвратит операционной системе

целочисленное значение.

Объект cout используется для вывода сообщений на экран, а объект cin

обрабатывает ввод с клавиатуры.

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

объекта класса kvartira необходим один параметр, то это будет выглядеть так:

kvartira mmeb(18);

Объявляем переменную a, которой присваиваем тип int, с помощью cin вводим её

с клавиатуры. С помощью оператора if организуем условие:

if (a=1){}

После завершения составления программы, она была откомпилирована и запущена,

пример её выполнения представлен в приложении №2.

ВЫВОД

В объектно-ориентированном программировании особое внимание уделяется

представлению данных в программе. Первый шаг к решению проблем

программирования при помощи подхода ООП – это описание в программе данных на

основе её интерфейса – такое описание определяет то, как эти данные будут

использоваться. Далее проектируется класс, реализующий такой интерфейс. Как

правило собственные элементы данных служат для хранения информации, в то

время как общедоступные функции-элементы(или методы) обеспечивают только

доступ к данным. Такой класс определяет данные и методы в один модуль, а

статус собственных данных обеспечивает сокрытие данных.

Как правило, объявление класса разбивается на две части, которые обычно

содержаться в разных файлах. Все, что относится к объявлению класса,

помещается в заголовочный файл, при этом методы представлены прототипами

функций. Исходные программные коды, которые определяют функции-элементы,

помещаются в файл методов. Такой подход отделяет описание интерфейса от

деталей реализации. По существу для того, чтобы пользоваться классом,

достаточно знать только общедоступный интерфейс класса. Поскольку программа и

класс взаимодействуют между собой при помощи методов, определяющих интерфейс,

имеется возможность совершенствовать любую из частей по отдельности по

средствам этих методов, не задумываясь о возможности непредвиденных

взаимодействиях.

Программа, представленная в приложении №1, может найти свое эффективное

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

расстановки мебели в квартире, а также позволяет заметно сэкономить время на

подсчет мебели.

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

достаточно прост для понимания рядовому пользователя.

СПИСОК ЛИТЕРАТУРЫ

1. Либерти Дж. Освой самостоятельно С++ за 21 день: 3-е изд, пер. с англ.:

Уч. Пос. – М.: Издательский дом «Вильямс», 2000. – 816.: ил. – Парал. Тит.

Англ.

2. Страуструп Б. Язык программирования С++: 2-е изд, пер. с англ.

ПРИЛОЖЕНИЕ №1

#include<iostream>

using namespace std;

class kvartira

{

public:

kvartira(int initialrazmer);

~kvartira();

int Getrazmer();

void Setrazmer(int razmer);

void meb();

private:

int itsrazmer;

};

class mebel

{

public:

mebel(int initialkol);

~mebel();

int Getkol();

void Setkol(int kol);

void stol();

void stul();

void divan();

void tumba();

private:

int itskol;

};

kvartira::kvartira(int initialrazmer)

{

itsrazmer=initialrazmer;

}

kvartira::~kvartira()

{}

int kvartira::Getrazmer()

{

return itsrazmer;

}

void kvartira::Setrazmer(int razmer)

{

itsrazmer=razmer;

}

void kvartira::meb()

{

cout<<"komnata\n";

}

mebel::mebel(int initialkol)

{

itskol=initialkol;

}

mebel::~mebel()

{}

int mebel::Getkol()

{

return itskol;

}

void mebel::Setkol(int kol)

{

itskol=kol;

}

void mebel::stol()

{

cout<<"stol\n";

}

void mebel::stul()

{

cout<<"stul\n";

}

void mebel::divan()

{

cout<<"divan\n";

}

void mebel::tumba()

{

cout<<"tumba\n";

}

int main()

{

cout<<"Mebel v kvartire\n";

mebel sstol(10);

sstol.stol();

cout<<"\nV komnate stolko stolov: "<<sstol.Getkol()<<"\n";

mebel sstul(11);

sstul.stul();

cout<<"\nV komnate stolko stulev: "<<sstul.Getkol()<<"\n";

mebel ddivan(12);

ddivan.divan();

cout<<"\nV komnate stolko divanov: "<<ddivan.Getkol()<<"\n";

mebel ttumba(13);

ttumba.tumba();

cout<<"\nV komnate stolko tumb: "<<ttumba.Getkol()<<"\n";

kvartira mmeb(18);

mmeb.meb();

cout<<"\nRazmer komnati: "<<mmeb.Getrazmer()<<"metrov\n";

int a;

cout<<"Vi hotite dobavt mebeli? Yes(1) No(2)\n";

cin>>a;

if (a=1)

{

cout<<"Chto vi hotite dobavit? stol(1), stul(2), divan(3), tumba(4)";

int b;

cin>>b;

if (b=1)

{

mebel sstol(11);

sstol.stol();

cout<<"\nTeper v komnate stolko stolov: "<<sstol.Getkol()<<"\n";

}

else if (b=2)

{

mebel sstul(12);

sstul.stul();

cout<<"\nTeper v komnate stolko stulev: "<<sstul.Getkol()<<"\n";

}

else if (b=3)

{

mebel ddivan(13);

ddivan.divan();

cout<<"\nTeper v komnate stolko divanov: "<<ddivan.Getkol()<<"\n";

}

else if (b=4)

{

mebel ttumba(14);

ttumba.tumba();

cout<<"\nTeper v komnate stolko tumb: "<<ttumba.Getkol()<<"\n";

}

}

else if (a=2)

cout<<"Net tak net!\n";

}

Пример выполнения программы приведен в приложении № 2

ПРИЛОЖЕНИЕ №2

Пример выполнения программы из приложения №1

Mebel v kvartire

stol

V komnate stolko stolov: 10

stul

V komnate stolko stulev: 11

divan

V komnate stolko divanov: 12

tumba

V komnate stolko tumb: 13

komnata

Razmer komnati: 18 metrov

Vi hotite dobavt mebeli? Yes(1) No(2)\n

1

Chto vi hotite dobavit? stol(1), stul(2), divan(3), tumba(4) 3

Teper v komnate stolko divanov: 13



(C) 2009