Последние новости

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

Вот уже на протяжении нескольких лет страны Евросоюза постепенно переходят на электронные паспорта...

Мы начинаем

IT Библиотека начинает свою работу

Знаете ли вы что означает ICQ...

России наверно каждый человек,который пользуется Интернетом....

все новости

Доверяем

Реклама

1.32.Объектный подход, 08/02/2009

К списку статей

1.32.Объектный подход В этом разделе мы спроектируем и реализуем абстракцию массива, используя механизм классов С++. Первоначальный вариант будет поддерживать только массив элементов типа int. Впоследствии при помощи шаблонов мы расширим наш массив для поддержки любых типов данных. Первый шаг состоит в том, чтобы определить, какие операции будет поддерживать наш массив. Конечно, было бы заманчиво реализовать все мыслимые и немыслимые операции, но невозможно сделать сразу все на свете. Поэтому для начала определим то, что должен уметь наш массив: 1.обладать некоторыми знаниями о самом себе. Пусть для начала это будет знание собственного размера; 2.поддерживать операцию присваивания и операцию сравнения на равенство; 3.отвечать на некоторые вопросы, например: какова величина минимального и максимального элемента; содержит ли массив элемент с определенным значением; если да, то каков индекс первого встречающегося элемента, имеющего это значение; 4.сортировать сам себя. Пусть такая операция покажется излишней, все-таки реализуем ее в качестве дополнительного упражнения: ведь кому-то это может пригодиться. 5.Конечно, мы должны реализовать и базовые операции работы с массивом, а именно:Возможность задать размер массива при его создании. (Речь не идет о том, чтобы знать эту величину на этапе компиляции.) 6.Возможность проинициализировать массив некоторым набором значений. 7.Возможность обращаться к элементу массива по индексу. Пусть эта возможность реализуется с помощью стандартной операции взятия индекса. 8.Возможность обнаруживать обращения к несуществующим элементам массива и сигнализировать об ошибке. Не будем обращать внимание на тех потенциальных пользователей нашего класса, которые привыкли работать со встроенными массивами С и не считают данную возможность полезной— мы хотим создать такой массив, который был бы удобен в использовании даже самым неискушенным программистам на С++. Кажется, мы перечислили достаточно потенциальных достоинств нашего будущего массива, чтобы загореться желанием немедленно приступить к его реализации. Как же это будет выглядеть на С++? В самом общем случае объявление класса выглядит class classname { public: // набор открытых операций private: // закрытые функции, обеспечивающие реализацию следующим образом: }; class, public и private- это ключевые слова С++, a classname- имя, которое программист дал своему классу. Назовем наш проектируемый класс IntArray: на первом этапе этот массив будет содержать только целые числа. Когда мы научим его обращаться с данными любого типа, можно будет переименовать его в Array. Определяя класс, мы создаем новый тип данных. На имя класса можно ссылаться точно так же, как на любой встроенный описатель типа. Можно создавать объекты этого нового // статический объект типа IntArray типа аналогично тому, как мы создаем объекты встроенных типов: IntArray myArray; // указатель на динамический объект типа IntArray IntArray *pArray = new IntArray; Определение класса состоит из двух частей: заголовка (имя, предваренное ключевым словом class) и тела, заключенного в фигурные скобки. Заголовок без тела может // объявление класса IntArray служить объявлением класса. // без определения его class IntArray; Тело класса состоит из определений членов и спецификаторов доступа — ключевых слов public, private и protected. (Пока мы ничего не будем говорить об уровне доступа protected.) Членами класса могут являться функции, которые определяют набор действий, выполняемых классом, и переменные, содержащие некие внутренние данные, необходимые для реализации класса. Функции, принадлежащие классу, называют функциями-членами или, по-другому, методами класса. Вот набор методов класса IntArray: class IntArray { public: // операции сравнения: #2b bool operator== (const IntArray&) const; bool operator!= (const IntArray&) const; // операция присваивания: #2a IntArrayfc operator= (const IntArray&); int sizeO const; // #1 void sort(); // #4 int mint) const; // #3a int max() const; // #3b // функция find возвращает индекс первого // найденного элемента массива // или -1, если элементов не найдено int find (int value) const; // #3c private: // дальше идут закрытые члены, // обеспечивающие реализацию класса } """ Номера, указанные в комментариях при объявлениях методов, ссылаются на спецификацию класса, которую мы составили в начале данного раздела. Сейчас мы не будем объяснять смысл ключевого слова const, он не так уж важен для понимания того, что мы хотим продемонстрировать на данном примере. Будем считать, что это ключевое слово необходимо для правильной компиляции программы. Именованная функция-член (например, min()) может быть вызвана с использованием одной из двух операций доступа к члену класса. Первая операция доступа, обозначаемая точкой (.), применяется к объектам класса, вторая— стрелка (->)— к указателям на объекты. Так, чтобы найти минимальный элемент в объекте, имеющем тип IntArray, мы // инициализация переменной min_val должны написать: int min_val = myArray.min(); I // минимальным элементом myArray Чтобы найти минимальный элемент в динамически созданном объекте типа IntArray, мы должны написать: int min_val = pArray->min(); (Да, мы еще ничего не сказали о том, как же проинициализировать наш объект — задать его размер и наполнить элементами. Для этого служит специальная функция-член, называемая конструктором. Мы поговорим об этом чуть ниже.) Операции применяются к объектам класса точно так же, как и к встроенным типам данных. Пусть мы имеем два объекта типа IntArray IntArray myArrayO, myArrayl; Инструкции присваивания и сравнения с этими объектами выглядят совершенно // инструкция присваивания - // вызывает функцию-член myArrayO.operator=(myArrayl) myArrayO = myArrayl; // инструкция сравнения - // вызывает функцию-член myArrayO.operator==(myArrayl) if (myArrayO == myArrayl) обычным образом: cout << "Ура! Оператор присваивания сработал! "; Спецификаторы доступа public и private определяют уровень доступа к членам класса. К тем членам, которые перечислены после public, можно обращаться из любого места программы, а к тем, которые объявлены после private, могут обращаться только функции-члены данного класса. (Помимо функций-членов, существуют еще функции-друзья класса, но мы не будем говорить о них вплоть до раздела 15.2.) В общем случае открытые члены класса составляют его открытый интерфейс, то есть набор операций, которые определяют поведение класса. Закрытые члены класса обеспечивают его скрытую реализацию. Такое деление на открытый интерфейс и скрытую реализацию называют сокрытием информации, или инкапсуляцией. Это очень важная концепция программирования, мы еще поговорим о ней в следующих главах. В двух словах, эта концепция помогает решить следующие проблемы: • если мы меняем или расширяем реализацию класса, то изменения можно выполнить так, что большинство пользовательских программ, использующих наш класс, их "не заметят": модификации коснутся лишь скрытых членов (мы поговорим об этом в разделе 6.18); • если в реализации класса обнаруживается ошибка, то обычно для ее исправления достаточно проверить код, составляющий именно скрытую реализацию, а не весь код программы, где данный класс используется. Какие же внутренние данные потребуются для реализации класса IntArray? Необходимо где-то сохранить размер массива и сами его элементы. Мы будем хранить их в массиве встроенного типа, память для которого выделяется динамически. Так что нам потребуется class IntArray { public: // ... int size() const { return _size; } private: // внутренние данные-члены указатель на этот массив. Вот как будут выглядеть определения этих данных-членов: int _size; int *ia; }; Поскольку мы поместили член _size в закрытую секцию, пользователь класса не имеет возможности обратиться к нему напрямую. Чтобы позволить внешней программе узнать размер массива, мы написали функцию-член size (), которая возвращает значение члена _size. Нам пришлось добавить символ подчеркивания к имени нашего скрытого члена _size, поскольку функция-член с именем size() уже определена. Члены класса-функции и данные — не могут иметь одинаковые имена. Может показаться, что реализуя подобным образом доступ к скрытым данным класса, мы очень сильно проигрываем в эффективности. Сравним два выражения (предположим, что IntArray array; мы изменили спецификатор доступа члена _size на public): int array_size = array.size(); array_size = array._size; Действительно, вызов функции гораздо менее эффективен, чем прямой доступ к памяти, как во втором операторе. Так что же, принцип сокрытия информации заставляет нас жертвовать эффективностью? На самом деле, нет. С++ имеет механизм встроенных (inline) функций. Текст встроенной функции подставляется компилятором в то место, где записано обращение к ней. (Это напоминает механизм макросов, реализованный во многих языках, в том числе и в С++. Однако есть определенные отличия, о которых мы сейчас говорить не будем.) for (int index=0; index int min (const int *pia,int size); int min (int, int) ; int min (const char *str); char min (string); функции-члены.) string min (string,string); Поведение перегруженных функций во время выполнения ничем не отличается от поведения обычных. Компилятор определяет нужную функцию и помещает в объектный код именно ее вызов. (В главе 9 подробно обсуждается механизм перегрузки.) Итак, вернемся к нашему классу IntArray. Давайте определим для него три class IntArray { public: explicit IntArray (int sz = DefaultArraySize); IntArray (int *array, int array_size); IntArray (const IntArray &rhs); // ... private: static const int DefaultArraySi ze = 12; конструктора: } Первый из перечисленных конструкторов IntArray (int sz = DefaultArraySize); называется конструктором no умолчанию, потому что он может быть вызван без параметров. (Пока не будем объяснять ключевое слово explicit.) Если при создании объекта ему задается параметр типа int, например IntArray arrayl(1024); то значение 1024 будет передано в конструктор. Если же размер не задан, допустим: IntArray array2; то в качестве значения отсутствующего параметра конструктор принимает величину Def aultArraySize. (Не будем пока обсуждать использование ключевого слова static в определении члена Def aultArraySize: об этом говорится в разделе 13.5. Скажем лишь, что такой член данных существует в единственном экземпляре и принадлежит одновременно всем объектам данного класса.) IntArray::IntArray (int sz) { // инициализация членов данных _size = sz; ia = new int[_size]; // инициализация элементов массива for (int ix=0; ix<_size; ++ix) ia[ix] = 0; Вот как может выглядеть определение нашего конструктора по умолчанию: } Это определение содержит несколько упрощенный вариант реализации. Мы не позаботились о том, чтобы попытаться избежать возможных ошибок во время выполнения. Какие ошибки возможны? Во-первых, оператор new может потерпеть неудачу при выделении нужной памяти: в реальной жизни память не бесконечна. (В разделе 2.6 мы увидим, как обрабатываются подобные ситуации.) А во-вторых, параметр sz из-за небрежности программиста может иметь некорректное значение, например нуль или отрицательное. Что необычного мы видим в таком определении конструктора? Сразу бросается в глаза первая строчка, в которой использована операция разрешения области видимости (: :): IntArray::IntArray(int sz); Дело в том, что мы определяем нашу функцию-член (в данном случае конструктор) вне тела класса. Для того чтобы показать, что эта функция на самом деле является членом класса IntArray, мы должны явно предварить имя функции именем класса и двойным двоеточием. (Подробно области видимости разбираются в главе 8; области видимости применительно к классам рассматриваются в разделе 13.9.) Второй конструктор класса IntArray инициализирует объект IntArray значениями элементов массива встроенного типа. Он требует двух параметров: массива встроенного типа со значениями для инициализации и размера этого массива. Вот как может int ia[10] = {0,1,2,3,4,5,6,7,8,9}; выглядеть создание объекта IntArray с использованием данного конструктора: IntArray iA3(ia,10);

Автор: , количество прочтений: 5703 Наверх


  • 1.31.Динамическое выделение памяти и указателиПрежд...
  • 1.30. Краткий обзор С++.Встроенный тип данных "массив"Как б...
  • 1.29. Файловый ввод/выводБибли...
  • 1.28. Первый взгляд на ввод/выводЧасть...
  • 1.27.Немного о комментарияхКомме...
  • 1.26. Директивы препроцессораЗагол...
  • 1.25.Порядок выполнения инструкцийПо ум...
  • 1.24.Программа на языке С++.Часть 2Пошаг...
  • 1.24.Программа на языке С++.Часть 2Пошаг...
  • 1.23.Программа на языке С++.Часть 1В С++...
  • 1.22.Решение задачиПрогр...
  • 1.21.C++ для начинающих.Часть I.Краткий обзор языкаПрогр...
  • 20.Массивы символов в C++ обзор.В C++...
  • 19.Массивы.Строки и управление вводом/выводом.C++ и...
  • 18.Массивы.Многомерные массивы.В мно...
  • 17.Массивы.Совок...
  • 16.Рекурсия.Рекур...
  • 15.Функции.Статические переменные.Локал...
  • 14.Функции.Локальные переменные.Одно ...
  • 13.Функции.Объявления и прототипы функции.В пра...
  • 12.Основные конструкции языка С++.Пропуск итерации числа.С++ п...
  • 11.Основные конструкции языка С++.Циклы.Вычис...
  • 10.Основные конструкции языка С++.Конструкции принятия решении и циклы.

    Ко...

  • 9.Препроцессор, переменные и операции языка С++.Арифметические выражения.

    Ар...

  • 8.Препроцессор, переменные и операции языка С++.Операции и выражения. Обраб...
  • 7.Препроцессор, переменные и операции языка С++.Объявление констант.Конст...
  • 6.Препроцессор, переменные и операции языка С++.Потоковый ввод/вывод.Поток...
  • 5.Препроцессор, переменные и операции языка С++.Объявления переменных.Объяв...
  • 4.Препроцессор, переменные и операции языка С++.Препроцессор.На за...
  • 3.Препроцессор, переменные и операции языка С++.Категории типов данных.В С++...
  • 2.Структура программы, особенности, синтаксис языка С++.Элементы языка.1. &n...
  • 1.Структура программы, особенности, синтаксис языка С++.Структура программы на языке С++.

    Пр...

  • Новые фотоальбомы наших пользователей ; Эффективная, высоко оборотистая бензопила для садовых работ в строительных магазинах России. ; Аренда складов производственных. Сниму офис,склад. Отапливаемый склад.