|
Программирование >> Инициализация объектов класса, структура
myArray0 = инструкция присваивания вызывает - функцию-член myArray0.operator=(myArray1) myArray1; инструкция сравнения - вызывает функцию-член myArray0.operator==(myArray1) if (myArray0 == myArray1) обычным образом: cout << Ура! Оператор присваивания сработал!\n ; Спецификаторы доступа public и private определяют уровень доступа к членам класса. К тем членам, которые перечислены после public, можно обращаться из любого места программы, а к тем, которые объявлены после private, могут обращаться только функции-члены данного класса. (Помимо функций-членов, существуют еще функции-друзья класса, но м1 не будем говорить о них вплоть до раздела 15.2.) В общем случае открытые члены класса составляют его открытый интерфейс, то есть набор операций, которые определяют поведение класса. Закрытые члены класса обеспечивают его скрытую реализацию. Такое деление на открытый интерфейс и скрытую реализацию называют сокрытием информации, или инкапсуляцией. Это очень важная концепция программирования, м1 еще поговорим о ней в следующих главах. В двух словах, эта концепция помогает решить следующие проблемы: если мы меняем или расширяем реализацию класса, то изменения можно выполнить так, что большинство пользовательских программ, использующих наш класс, их не заметят : модификации коснутся лишь скрытых членов (мы поговорим об этом в разделе 6.18); если в реализации класса обнаруживается ошибка, то обычно для ее исправления достаточно проверить код, составляющий именно скрытую реализацию, а не весь код программы, где данный класс используется. Какие же внутренние данные потребуются для реализации класса IntArray? Необходимо где-то сохранить размер массива и сами его элементы. Мы будем хранить их в массиве встроенного типа, память для которого выделяется динамически. Так что нам потребуется class IntArray { public: ... int size() const { return size; } private: внутренние данные-члены указатель на этот массив. Вот как будут выглядеть определения этих данных-членов: int size; int *ia; IntArray myArray0, myArray1; Инструкции присваивания и сравнения с этими объектами выглядят совершенно IntArray array; мы изменили спецификатор доступа члена size на public): int array size = array.size(); array size = array. size; Действительно, вызов функции гораздо менее эффективен, чем прямой доступ к памяти, как во втором операторе. Так что же, принцип сокрытия информации заставляет нас жертвовать эффективностью? На самом деле, нет. С++ имеет механизм встроенных (inline) функций. Текст встроенной функции подставляется компилятором в то место, где записано обращение к ней. (Это напоминает механизм макросов, реализованный во многих языках, в том числе и в С++. Однако есть онределенн1е отличия, о которых м1 сейчас говорить не будем.) for (int index=0; index<array.size(); ++index) Вот пример. Если у нас есть следующий фрагмент кода: ... то функция size() не будет вызываться size раз во время исполнения. Вместо вызова компилятор подставит ее текст, и результат компиляции предыдущего кода будет в точности таким же, как если бы мы написали: for (int index=0; index<array. size; ++index) ... Если функция определена внутри тела класса (как в нашем случае), она автоматически считается встроенной. Существует также ключевое слово inline, позволяющее объявить встроенной любую функцию3. Мы до сих пор ничего не сказали о том, как будем инициализировать наш массив. Одна из самых распространенных ошибок при программировании (на любом языке) состоит в том, что объект используется без предварительной инициализации. Чтобы помочь избежать этой ошибки, С++ обеспечивает механизм автоматической инициализации для определяемых пользователем классов - конструктор класса. 3 Объявление функции inline - это всего лишь подсказка компилятору. Однако компилятор не всегда может сделать функцию встроенной, существуют некоторые ограничения. Подробнее об этом сказано в разделе 7.6. Поскольку мы поместили член size в закрытую секцию, пользователь класса не имеет возможности обратиться к нему напрямую. Чтобы позволить внешней программе узнать размер массива, мы написали функцию-член size() , которая возвращает значение члена size. Нам пришлось добавить символ подчеркивания к имени нашего скрытого члена size, поскольку функция-член с именем size() уже определена. Члены класса -функции и данные - не могут иметь одинаковые имена. Может показаться, что реализуя подобным образом доступ к скрытым данным класса, мы очень сильно проигрываем в эффективности. Сравним два выражения (предположим, что список перегруженн функций min() каждая функция отличается от других списком параметров size); #include <string> int min (const int *pia,int 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 = DefaultAArraySize); IntAArray (int *array, int array size) ; IntArray (const IntArray Srhs); ... private: static const int DefaultArraySize = 12; конструктора: Первый из перечисленных конструкторов IntAArray (int sz = DefaultArraySize); называется конструктором по умолчанию, потому что он может быть вызван без параметров. (Пока не будем объяснять ключевое слово explicit.) Если при создании объекта ему задается параметр типа int, например Конструктор - это специальная функция-член, которая вызывается автоматически при создании объекта типа класса. Конструктор пишется разработчиком класса, причем у одного класса может быть несколько конструкторов. Функция-член класса, носящее то же имя, что и сам класс, считается конструктором. (Нет никаких специальных ключевых слов, позволяющих определить конструктор как-то по-другому.) М1 уже сказали, что конструкторов может быть несколько. Как же так: разные функции с одинаковыми именами? В С++ это возможно. Разные функции могут иметь одно и то же имя, если у этих функций различны количество и/или типы параметров. Это называется перегрузкой функции. Обрабатывая вызов перегруженной функции, компилятор смотрит не только на ее имя, но и на список параметров. По количеству и типам передаваемых параметров компилятор может определить, какую же из одноименных функций нужно вызывать в данном случае. Рассмотрим пример. Мы можем определить следующий набор перегруженных функций min() . (Перегружаться могут как обычные функции, так и
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |