Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции C++.doc
Скачиваний:
0
Добавлен:
06.02.2020
Размер:
1.44 Mб
Скачать

120

Глава 1. Обзор C++

Язык программирования C++ является объектно-ориентированным. Различные аспекты объект­но-ориентированного программирования в языке C++ переплетены между собой. Поэтому, прежде чем изучать нюансы, важно получить общее представление об особенностях объектно-ориентиро­ванного программирования. Задачей этой главы является обзор ключевых концепций, включен­ных в C++. Остальные главы посвящены подробному изучению специфических особеннос­тей C++.

1.1 Происхождение C++

C++ является расширением языка С. С представляет собой гибкий и мощный язык программиро­вания, использовавшийся для разработки наиболее важных программных продуктов в течение прошедших нескольких лет. Однако, как только проект превышает определенные размеры, воз­можности применения языка С достигают своих границ. В зависимости от проекта, программы размером от 25000 до 100000 строк оказываются трудными для разработки и управления потому, что их трудно охватить целиком. Работая в Bell Laboratories в Murray Hill, штат Нью-Джерси, Бьярн Страуструп (Bjarne Stroustrup) добавил к языку С несколько расширений с целью решить эту проблему. Первоначально язык назывался “С с классами”. Это название было заменено на C++ в 1983 году.

Большинство сделанных Страуструпом добавлений к С поддерживают объектно-ориентирован­ное программирование, которое сокращенно называют ООП. Как отмечает Стра­уструп, целый ряд объектно-ориентированных концепций был добавлен в C++, основываясь на языке Симула-6. Поэтому C++ представляет собой смесь двух мощных программных методов.

С момента своего возникновения C++ подвергался серьезным ревизиям трижды, первый раз в 1985 году, второй — в 1989 году. Третий пересмотр языка произошел в связи с работой над стандартом ANSI для C++. Первая версия предложенного стандарта была создана к 25 января 1994 года. Комитет ANSI по языку C++ практически сохранил все черты языка, определенные Страуструпом, и добавил несколько новых. Процесс стандартизации обычно является достаточно медленным, и стандартизация C++ не является исключением. Работа над C++ продол­жается и некоторые его характеристики могут быть модифицированы.

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

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

1.2 Философские замечания

Язык программирования решает две взаимосвязанные задачи: позволяет программисту записать подлежащие выполнению действия и формирует понятия, которыми программист оперирует, размышляя о своей задаче. Первой цели идеально отвечает язык, который очень "близок машине". Тогда со всеми ее основными "сущностями" можно просто и эффективно работать на этом языке, причем делая это очевидным для программиста способом. Именно это имели в виду создатели С. Второй цели идеально отвечает язык, который настолько "близок к поставленной задаче", что на нем непосредственно и точно выражаются понятия, используемые в решении задачи. Именно это имелось в виду, когда первоначально определялись средства, добавляемые к С.

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

- лучше языка С;

- поддерживать абстракцию данных;

- поддерживать объектно-ориентированное программирование.

В этой главе объясняется смысл этих фраз без подробного описания конструкций языка.

1.3 Процедурное программирование

Первоначальной (и, возможно, наиболее используемой) парадигмой программирования было:

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

Ударение делалось на обработку данных с помощью алгоритма, производящего нужные вычисления. Для поддержки этой парадигмы языки предоставляли механизм передачи параметров и получения результатов функций. Первым процедурным языком был Фортран, а Алгол60, Алгол68, Паскаль и С продолжили это направление.

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

double sqrt(double arg)

{

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

}

voide some_function()

{

double root = sqrt(2);

// ..

}

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

1.4 Модульное программирование

Со временем при проектировании программ акцент сместился с организации процедур на организацию структур данных. Помимо всего прочего это вызвано и ростом размеров программ. Модулем обычно называют совокупность связанных процедур и тех данных, которыми они управляют. Парадигма программирования приобрела вид:

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

Эта парадигма известна также как "принцип сокрытия данных". Если в языке нет возможности сгруппировать связанные процедуры вместе с данными, то он плохо поддерживает модульный стиль программирования. Теперь метод написания "хороших" процедур применяется для отдельных процедур модуля. Типичный пример модуля - определение стека. Здесь необходимо решить такие задачи:

1) Предоставить пользователю интерфейс для стека (например, функции push() и pop()).

2) Гарантировать, что представление стека (например, в виде массива элементов) будет доступно лишь через интерфейс пользователя.

3) Обеспечивать инициализацию стека перед первым его использованием.

Язык Модула-2 прямо поддерживает эту парадигму, тогда как С только допускает такой стиль. Ниже представлен на С возможный внешний интерфейс модуля, реализующего стек:

void push(char);

char pop();

const int stack_size = 100;

Допустим, что описание интерфейса находится в файле stack.h, тогда реализацию стека можно определить следующим образом:

#include "stack.h"

static char v[stack_size]; // “static” означает: локальный в данном файле/модуле

static char *p = v; // стек вначале пуст

void push(char c)

{

//проверить на переполнение и поместить в стек

}

char pop()

{

//проверить, не пуст ли стек, и считать из него

}

Вполне возможно, что реализация стека может измениться, например, если использовать для хранения связанный список. Пользователь в любом случае не имеет непосредственного доступа к реализации: v и p - статические переменные, т.е. переменные, локальные в том модуле (файле), в котором они описаны. Использовать стек можно так:

#include "stack.h" // используем интерфейс стека

void some_function()

{

push('c');

char c = pop();

if (c != 'c') error("невозможно");

}

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