Объектно-ориентированное программирование, анализ и дизайн. Методическое пособие [В. В. Мухортов] (pdf) читать постранично, страница - 4

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

во-вторых, обеспечить
защиту внутренних деталей реализации от потенциального воздействия
других компонент программ.
Рассмотрим решение этой задачи средствами процедурного языка
C, далее решим задачу с использованием средств поддержки модульности языка C++, и, наконец, предложим объектную реализацию стека на C++. Для простоты наш стек будет хранить нетипизированные
указатели на void*. Это, безусловно не лучшее решение, но в нашем
случае цель состоит в демонстрации разных подходов, а не написании
профессионального кода для практического использования. Для того,
чтобы не загромождать пример лишними подробностями, мы опустим
обработку ошибок выделения памяти.
В случае использования механизмов языка C, стратегия может быть
следующей — мы объявляем набор операций для работы со стеком и
заводим некоторую структуру для хранения его содержимого. Ниже
приведен пример заголовочного файла для стека, а также файл модуля,
где реализованы функции работы со стеком, объявленные в заголовке.
/* file simple_stack.h */
#ifndef _SIMPLE_STACK_H
#define _SIMPLE_STACK_H
#define MAX_STACK_SIZE 200
typedef struct {
int top;
void* content[MAX_STACK_SIZE];
} SimpleStack;
/* returns -1 if overflow */
int push(SimpleStack *pstack, void *pobj);
/* returns NULL if empty */
void* pop(SimpleStack *pstack);

1.2. ЗАРОЖДЕНИЕ ОБЪЕКТНОЙ МОДЕЛИ
SimpleStack* allocateStack();
void freeStack(SimpleStack *pstack);
#endif /*_SIMPLE_STACK_H */
/* file simple_stack.c */
#include
#include
#include "simple_stack.h"
SimpleStack* allocateStack()
{
SimpleStack* pstack =
(SimpleStack*)malloc(sizeof (SimpleStack));
pstack->top = 0;
return pstack;
}
void freeStack(SimpleStack *pstack)
{
free(pstack);
}
int push(SimpleStack *pstack, void *pobj)
{
if (pstack->top < MAX_STACK_SIZE) {
pstack->content[(pstack->top)++] = pobj;
return 0;
} else {
return -1; /* overflow */
}
}
void* pop(SimpleStack *pstack)
{
if(pstack->top > 0) {
return pstack->content[–(pstack->top)];
} else {
return NULL; /* underflow */

15

16ГЛАВА 1. ЭВОЛЮЦИЯ МЕТОДОЛОГИЙ ПРОГРАММИРОВАНИЯ
}
}

Данная реализация имеет множество недостатков, хотя и может сгодиться для небольших программ. Главными являются следующие два:
• Данные экземпляров структуры не защищены от недобросовестного использования. Следующий код может непоправимо нарушить работу программы:
SimpleStack pStack = allocateStack();
pStack->top = -10;

• Стек реализован в виде массива, и нет простого способа заменить его реализацию в программах, уже использующих данную
версию.
Мы можем существенно усовершенствовать наш стек, если воспользуемся средствами модульного программирования, имеющимися
в языке С++.
// Файл advanced_stack.h
// Усовершенствованный вариант стека, использующий
// механизм модульности языка C++
#ifndef _ADVANCED_STACK_H
#define _ADVANCED_STACK_H
// Таким образом мы определяем функциональный
// контракт модульной реализации стека
namespace advanced_stack {
struct Stack_impl;
typedef struct Stack_impl AdvancedStack;
// returns -1 if overflow
int push(AdvancedStack *pstack, void *pobj);
// returns NULL if empty
void* pop(AdvancedStack *pstack);

1.2. ЗАРОЖДЕНИЕ ОБЪЕКТНОЙ МОДЕЛИ
AdvancedStack* allocateStack();
void freeStack(AdvancedStack* pstack);
}
#endif // _ADVANCED_STACK_H

// Файл advanced_stack.cpp
// Реализация контракта модульной реализации стека
#include "advanced_stack.h"
namespace advanced_stack
{
const int MAX_STACK_SIZE = 200;
struct Stack_impl {
int top;
void* content[MAX_STACK_SIZE];
};
Stack_impl* allocateStack()
{
Stack_impl *pstack = new Stack_impl();
pstack->top = 0;
return pstack;
}
void freeStack(Stack_impl *pstack)
{
delete pstack;
}
int push(Stack_impl *pstack, void *pobj)
{
if (pstack->top < MAX_STACK_SIZE) {
pstack->content[(pstack->top)++] = pobj;
return 0;
} else {
return -1; // overflow

17

18ГЛАВА 1. ЭВОЛЮЦИЯ МЕТОДОЛОГИЙ ПРОГРАММИРОВАНИЯ
}
}
void* pop(Stack_impl *pstack)
{
if(pstack->top > 0) {
return pstack->content[–(pstack->top)];
} else {
return 0; // underflow
}
}
}

В данной реализации решены обе упомянутые выше проблемы.
Примером использования модульной организации стека может стать
следующая программа:
#include
#include "advanced_stack.h"
int main()
{
using namespace advanced_stack;
AdvancedStack *pstack = allocateStack();
//pstack->top = 10; // Данная строка вызвала бы ошибку компиляции.
char *letters [3] = {"one","two","three"};
int i;
push(pstack, (void*) (letters[0]));
push(pstack, (void*) (letters[1]));
push(pstack, (void*) (letters[2]));
for (i = 0; i < 3; i++) {
printf("\n%s\n", (char*)pop(pstack));
}
freeStack(pstack);
}

Если в будущем потребуется заменить реализацию стека, то это можно
сделать изменив реализацию структуры Stack_impl и функции работы

1.2. ЗАРОЖДЕНИЕ ОБЪЕКТНОЙ МОДЕЛИ

19

с ней, реализованные в файле advanced_stack.cpp. Также можно объявить новое пространство имен (namespace), например dynamic_stack,
в котором будет реализован стек изменяемого размера. В прежней программе, использующей модульную реализацию стека, достаточно будет заменить строку
using namespace advanced_stack;

на строку
using namespace dynamic_stack;

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