С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 10
Текст из файла (страница 10)
Как же так: разныефункции с одинаковыми именами?В С++ это возможно. Разные функции могут иметь одно и то же имя, если у этихфункций различны количество и/или типы параметров. Это называется перегрузкойфункции. Обрабатывая вызов перегруженной функции, компилятор смотрит не только наее имя, но и на список параметров. По количеству и типам передаваемых параметровкомпилятор может определить, какую же из одноименных функций нужно вызывать вданном случае. Рассмотрим пример. Мы можем определить следующий наборперегруженных функций min().
(Перегружаться могут как обычные функции, так и// список перегруженных функций min()// каждая функция отличается от других списком параметров#include <string>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 DefaultArraySize = 12;конструктора:}Первый из перечисленных конструкторовIntArray (int sz = DefaultArraySize);называется конструктором по умолчанию, потому что он может быть вызван безпараметров.
(Пока не будем объяснять ключевое слово explicit.) Если при созданииобъекта ему задается параметр типа int, например41С++ для начинающихIntArray array1(1024);то значение 1024 будет передано в конструктор. Если же размер не задан, допустим:IntArray array2;то в качестве значения отсутствующего параметра конструктор принимает величинуDefaultArraySize. (Не будем пока обсуждать использование ключевого слова static вопределении члена DefaultArraySize: об этом говорится в разделе 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);42С++ для начинающихРеализация второго конструктора очень мало отличается от реализации конструктора поIntArray::IntArray (int *array, int sz){// инициализация членов данных_size = sz;ia = new int[_size];// инициализация элементов массиваfor (int ix=0; ix<_size; ++ix)ia[ix] = array[ix];умолчанию.
(Как и в первом случае, мы пока опустили обработку ошибочных ситуаций.)}Третий конструктор называется копирующим конструктором. Он инициализирует одинобъект типа IntArray значением другого объекта IntArray. Такой конструкторвызывается автоматически при выполнении следующих инструкций:IntArray array;// следующие два объявления совершенно эквивалентны:IntArray ia1 = array;IntArray ia2 (array);Вот как выглядит реализация копирующего конструктора для IntArray, опять-таки безIntArray::IntArray (const IntArray &rhs )обработки ошибок:{// инициализация членов данных_size = rhs._size;ia = new int[_size];// инициализация элементов массиваfor (int ix=0; ix<_size; ++ix)ia[ix] = rhs.ia[ix];}В этом примере мы видим еще один составной тип данных – ссылку на объект, котораяобозначается символом &.
Ссылку можно рассматривать как разновидность указателя:она также позволяет косвенно обращаться к объекту. Однако синтаксис их использованияразличается: для доступа к члену объекта, на который у нас есть ссылка, следуетиспользовать точку, а не стрелку; следовательно, мы пишем rhs._size, а не rhs->_size.(Ссылки рассматриваются в разделе 3.6.)Заметим, что реализация всех трех конструкторов очень похожа. Если один и тот же кодповторяется в разных местах, желательно вынести его в отдельную функцию. Этооблегчает и дальнейшую модификацию кода, и чтение программы.
Вот как можномодернизировать наши конструкторы, если выделить повторяющийся код в отдельнуюфункцию init():43С++ для начинающихclass IntArray {public:explicit IntArray (int sz = DefaultArraySize);IntArray (int *array, int array_size);IntArray (const IntArray &rhs);// ...private:void init (int sz,int *array);// ...};// функция, используемая всеми конструкторамиvoid IntArray::init (int sz,int *array){_size = sz;ia = new int[_size];for (int ix=0; ix<_size; ++ix)if ( !array )ia[ix] = 0;elseix[ix] = array[ix];}// модифицированные конструкторыIntArray::IntArray (int sz) { init(sz,0); }IntArray::IntArray (int *array, int array_size){ init (array_size,array); }IntArray::IntArray (const IntArray &rhs){ init (rhs._size,rhs.ia); }Имеется еще одна специальная функция-член – деструктор, который автоматическивызывается в тот момент, когда объект прекращает существование.
Имя деструкторасовпадает с именем класса, только в начале идет символ тильды (~). Основное назначениеданной функции – освободить ресурсы, отведенные объекту во время его создания ииспользования. Применение деструкторов помогает бороться с трудно обнаруживаемымиошибками, ведущими к утечке памяти и других ресурсов. В случае класса IntArray этафункция-член должна освободить память, выделенную в момент создания объекта.(Подробно конструкторы и деструкторы описаны в главе 14.) Вот как выглядитclass IntArray {деструктор для IntArray:public:// конструкторыexplicit IntArray (int sz = DefaultArraySize);IntArray (int *array, int array_size);IntArray (const IntArray &rhs);// деструктор~IntArray() { delete[] ia; }// ...private:// ...};44С++ для начинающихТеперь нам нужно определить операции доступа к элементам массива IntArray.
Мыхотим, чтобы обращение к элементам IntArray выглядело точно так же, как к элементамIntArray array;массива встроенного типа, с использованием оператора взятия индекса:int last_pos = array.size()-1;int temp = array[0];array[0] = array[last_pos];array[last_pos] = temp;Для реализации доступа мы используем возможность перегрузки операций. Вот как#include <cassert>int& IntArray::operator[] (int index){assert (index >= 0 && index < _size);return ia[index];выглядит функция, реализующая операцию взятия индекса:}Обычно для проектируемого класса перегружают операции присваивания, операциюсравнения на равенство, возможно, операции сравнения по величине и операцииввода/вывода.
Как и перегруженных функций, перегруженных операторов,отличающихся типами операндов, может быть несколько. К примеру, можно создатьнесколько операций присваивания объекту значения другого объекта того же самого илииного типа. Конечно, эти объекты должны быть более или менее “похожи”. (Подробно оперегрузке операций мы расскажем в главе 15, а в разделе 3.15 приведем еще несколькопримеров.)Определения класса, различных относящихся к нему констант и, быть может, каких-тоеще переменных и макросов по принятым соглашениям помещаются в заголовочныйфайл, имя которого совпадает с именем класса. Для класса IntArray мы должны создатьзаголовочный файл IntArray.h. Любая программа, в которой будет использоватьсякласс IntArray, должна включать этот заголовочный файл директивой препроцессора#include.По тому же самому соглашению функции-члены класса, определенные вне его описания,помещаются в файл с именем класса и расширением, обозначающим исходный текст С++программы.
Мы будем использовать расширение .С (напомним, что в разных системахвы можете встретиться с разными расширениями исходных текстов С++ программ) иназовем наш файл IntArray.C.Упражнение 2.5Ключевой особенностью класса С++ является разделение интерфейса и реализации.Интерфейс представляет собой набор операций (функций), выполняемых объектом; онопределяет имя функции, возвращаемое значение и список параметров. Обычнопользователь не должен знать об объекте ничего, кроме его интерфейса. Реализацияскрывает алгоритмы и данные, нужные объекту, и может меняться при развитии объекта,45С++ для начинающихникак не затрагивая интерфейс. Попробуйте определить интерфейсы для одного изследующих классов (выберите любой):(a) матрица(b) булевское значение(c) паспортные данные человека(d) дата(e) указатель(f) точкаУпражнение 2.6Попробуйте определить набор конструкторов, необходимых для класса, выбранного вамив предыдущем упражнении.
Нужен ли деструктор для вашего класса? Помните, что насамом деле конструктор не создает объект: память под объект отводится до начала работыданной функции, и конструктор только производит определенные действия поинициализации объекта. Аналогично деструктор уничтожает не сам объект, а только тедополнительные ресурсы, которые могли быть выделены в результате работыконструктора или других функций-членов класса.Упражнение 2.7В предыдущих упражнениях вы практически полностью определили интерфейсвыбранного вами класса.