Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Передача параметра по ссылке



. В С для организации вызова по ссылке программисты используют указатели и операцию косвенной адресации. Если вызывается функция, аргументы кото­рой должны изменяться, то в этом случае ей передаются адреса аргументов. Обычно для этой цели применяется операция взятия адреса & к переменной, значение которой будет изменяться. Если в качестве аргумента в функцию передается массив, то функция получает адрес начала массива и использовать операцию & в этом случае не нужно. Когда адрес переменной передан функции, то для изменения ее значения может быть использована операция косвенной адресации *.

Если функция может получить в качестве аргумента одномерный массив, то в функциональном заголовке и в прототипе функции соответствующий па­раметр может быть определен как указатель. Компилятор не делает различия между функцией, имеющей параметром указатель, и функцией, которая по­лучает в качестве аргумента одномерный массив. Однако понятно, что функ­ция должна «знать», когда она получает массив и когда ссылку на одиночную переменную. Когда компилятор встречает одномерный массив в качестве па­раметра функции, заданный в форме int b[], компилятор преобразует этот па­раметр к виду int *b. Эти две формы являются взаимозаменяемыми.

Массивы и указатели в С тесно связаны друг с другом и практически явля­ются взаимозаменяемыми. Имя массива можно рассматривать как указа­тель-константу. А над указателями можно выполнять различные операции, в том числе использовать с указателем индексные выражения.

В качестве примера объявим целочисленный массив b[5] и перемен­ную-указатель bPtr на целое. Так как имя массива (если с ним не указан ин­декс) является указателем на первый элемент массива, мы можем присвоить указателю bPtr адрес первого элемента массива b при помощи оператора при­сваивания bPtr = b;

Этот оператор эквивалентен следующему оператору, в котором использу­ется операция взятия адреса первого элемента массива: bPtr = & b[0];

Альтернативный способ ссылки на элемент массива b[3], использующий выражение с указателем, представлен в следующем операторе: * (bPtr + 3)

Константа 3 в приведенном выражении называется смещением. Когда указатель ссылается на начало массива, величина смещения указывает, на ка­кой элемент массива производится ссылка; значение смещения равно значе­нию индекса массива. Приведенный способ записи носит название нотации указатель/смещение. В этом выражении использованы круглые скобки, пото­му что операция * имеет больший приоритет, чем операция +. Без круглых скобок в вышеупомянутом выражении значение 3 было бы прибавлено к зна­чению выражения *bPtr (т.е. число 3 будет прибавлено к элементу b[0], так как bPtr указывает на начало массива). Поскольку на значение элемента мас­сива можно сослаться при помощи выражения с указателем, адрес элемента & b[3] представляется выражением bPtr + 3

Имя массива может рассматриваться как указатель, так что его можно ис­пользовать в выражениях арифметики указателей. Указатели, в свою очередь, могут быть использованы вместо имен масси­вов в индексных выражениях. Например, выражение bPtr[1] представляет собой ссылку на элемент массива b[1]. Такой способ записи мож­но назвать методом указатель/индекс.

Не забудьте, что имя массива - это указатель-константа и он всегда ука­зывает на начало массива. Поэтому выражение b += 3 является недопустимым, так как в нем делается попытка изменить значение начального адреса массива.

Массивы могут состоять из указателей. Обычный случай такого масси­ва - это массив строк, который так и называется массив строк. Элементом та­кого массива является строка, а строки в С являются, по существу, указателя­ми на первый символ строки. Значит, элементами строкового массива являют­ся указатели на начала строк. В качестве примера рассмотрим массив suit, ко­торый может пригодится для описания игральных карт.

char *suit[4] = {" Hearts", " Diamonds", " Clubs", " Spades" };

Выражение suit[4] в объявлении означает массив из четырех элементов. При помощи char * этот массив объявляется состоящим из указателей на тип char. В массив помещаются четыре значения «Hearts», «Diamonds», «Clubs» и «Spades» ( «Червы», «Бубны», «Трефы» и «Пики» ). Каждое из этих значе­ний хранится в памяти как строка символов с конечным нулем, длиной на один символ больше, чем количество символов, заключенных в кавычки. Строки эти занимают в памяти соответственно 7, 9, 6 и 7 байт. И хотя кажет­ся, что в массив помещаются сами строки, элементами массива являются ука­затели.

Каждый указатель ссылается на первый символ соответствующей строки. Таким образом, хотя массив suit имеет фиксированный размер, он может «хранить» символьные строки произвольной длины. Это еще один пример мощных возможностей структурирования данных в С.

Рассматриваемый нами массив карточных мастей можно было бы сделать двумерным: имя каждой масти занимало бы одну строку, а в каждом столбце помещалось бы по одному символу имени масти. При таком подходе все стро­ки массива должны быть одинаковой длины, равной размеру самой длинной строки символов. Это привело бы к неоправданному расходу памяти в случае, когда большинство сохраняемых строк короче, чем самая длинная строка.

Указатель на функцию - это переменная, содержащая адрес в памяти, по которому расположена функция. Мы знаем, что имя массива являет­ся адресом первого элемента массива. Аналогичным образом имя функции - это адрес начала программного кода функции. Указатели на функции могут быть переданы функциям в качестве аргументов, могут возвращаться функция­ми, сохраняться в массивах и присваиваться другим указателям на функции.

Структуры данных (Описание структур, Инициализация структур, Доступ к элементам структур, Использование структур с функциями, Typedef, Объединения, Структуры, ссылающиеся на себя, Динамическое распределение памяти, Связанные списки, Стеки, Очереди, Деревья)

Структуры — это наборы (иногда их называют агрегатами) логически связанных переменных, объединенных под одним именем. В отличие от мас­сивов, которые могут содержать элементы только одного типа, структуры мо­гут состоять из переменных различных типов данных Структуры — это производные типы данных, они создаются из объектов других типов. Рассмотрим следующее описание структуры; struct card{char *face; char *suit; }; Ключевое слово struct определяет структуру. Идентификатор card являет­ся именем-этикеткой структуры. Имя-этикетка именует структуру, и испо­льзуется совместно с ключевым словом struct для объявления переменных типа структуры. Перемен­ные, объявленные внутри скобок структуры, являются элементами структу­ры. Каждое определение структуры должно заканчиваться точкой с запятой. Определение struct card содержит два элемента типа char * - face и suit. Элементы структуры могут быть переменными основных типов данных (то есть int, float и т.д.), Объявление struct card a, deck[52], *cPtr; объявляет а - переменную типа struct card, deck - массив из 52 элементов типа struct card и cPtr - указатель на struct card.

Чтобы инициализировать структуру, после имени перемен­ной в объявлении структуры ставится знак равенства, за которым следует по­мещенный в фигурные скобки, разделенный запятыми список инициализато­ров. Например, объявление struct card a = {" Three", " Hearts" }; создает переменную а типа struct card (структура определена выше) и присваи­вает элементу face значение " Three", а элементу suit значение " Hearts", Если инициализаторов в списке меньше, чем элементов в структуре, остальным эле­ментам автоматически присваивается значение 0 (или NULL, если элемент — указатель). Для обращения к элементам структур используются две операции: опера­ция элемента структуры (.), также называемая операцией-точкой, и опера­ция указателя структуры (-> ), также называемая операцией-стрелкой. Опе­рация элемента структуры обращается к элементу через имя переменной структуры. Например, для того чтобы напечатать элемент suit структуры а из предыдущего объявления, можно написать оператор printf(" %s", a.suit); Операция указателя структуры, состоящая из знака минус (-) и знака бо­льше (> ) без пробела между ними, обращается к элементу через указатель структуры. Предположим, что переменная aPtr была объявлена как указатель на struct card и ей был присвоен адрес структуры а. Чтобы напечатать эле­мент suit структуры а при помощи указателя aPtr, напишите оператор printf (" %s", aPtr-> suit); Выражение aPtr-> suit эквивалентно (*aPtr).suit, которое, обращаясь по адресу, содержащемуся в указателе, находит структуру а и обращается к эле­менту suit, используя операцию элемента структуры.

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

Ключевое слово typedef предоставляет программисту механизм для созда­ния синонимов (или псевдонимов) для ранее определенных типов данных. Ча­сто используют typedef для того, чтобы дать укороченное имя структурному типу. Например, оператор typedef struct card Card; определяет новый тип с именем Card, как синоним типа struct card. Пишу­щие на С часто используют typedef, чтобы определить тип структуры, при этом отпадает необходимость в имени-этикетке, Создание нового имени с помощью typedef не создает нового типа; type­def просто определяет новое имя для уже существующего типа, которое может использоваться как псевдоним последнего.

Объединение - производный тип данных, подобный структуре, элементы которого разделяют одну и ту же область памяти. Элементы объединения могут принадлежать к любому типу. В большинстве случаев объединения со­держат два или более типа данных.. Задача программиста - обеспечить, чтобы на данные, хранящиеся в объединении, ссылались как на данные соответствующего типа. Объединение объявляется с помощью ключевого слова union. Формат объ­явления тот же, что и в случае структуры. Объявление union: union number{ int x; float y; }; означает, что number является типом union сэлементами int x и float у. Опре­деление объединения обычно располагается в программе перед функцией main, поэтому определение может использоваться для объявления переменных во всех функциях программы. Над объединениями можно выполнять следующие операции: присваива­ние объединения другому объединению того же типа, взятие адреса (& ), до­ступ к элементам объединения с использованием операций элемента структу­ры и указателя структуры. Объединения не могут сравниваться по тем же са­мым причинам, что и структуры.

Структуры, ссылающиеся на себя, содержат в качестве элемента указа­тель, который ссылается на структуру того же типа. Например, определение struct node{int data; struct node *nextPtr; };

описывает тип struct node. Структура типа struct node состоит из двух элемен­тов - целого data и указателя nextPtr. Элемент nextPtr указывает на структу­ру типа struct node - структуру того же самого типа, что и только что объявленная нами, отсюда и термин «структура, ссылающаяся на себя». Эле­мент nextPtr иногда называют связкой, т.е. nextPtr можно использовать для того, чтобы связать структуру типа struct node с другой структурой того же типа. Структуры, ссылающиеся на себя, могут связываться вместе для образо­вания полезных структур данных, таких как списки, очереди, стеки и деревья.

Создание и использование динамических структур данных требует дина­мического распределения памяти - возможности получать в процессе испол­нения дополнительную память для хранения новых узлов и освобождать бло­ки памяти, ставшие ненужными. Максимальный размер выделяемой динами­чески памяти определяется доступной физической памятью компьютера или доступным виртуальным адресным пространством в системе с виртуальной па­мятью. Однако часто эти размеры значительно меньше, потому что память разделяется между многими пользователями (задачами). Для динамического распределения памяти необходимо применение функ­ций malloc и free, атакже операции sizeof. Функция malloc принимает в ка­честве аргумента число байт, которое необходимо выделить, и возвращает ука­затель на выделенную память типа void*. Указатель void* можно присвоить любой переменной-указателю. Функция malloc обычно используется совмест­но с операцией sizeof. Например, оператор newPtr = malloc(sizeof(struct node));

оценивает sizeof(struct node) для определения размера в байтах структуры типа struct node, выделяет новый блок памяти размером в sizeof(struct node) байт, и сохраняет указатель на выделенную память в переменной newPtr. Если необходимого количества памяти нет в наличии, malloc возвращает указатель NULL.

Функция free освобождает память, т.е. память возвращается системе, и в дальнейшем ее можно выделить снова. Для высвобождения памяти, динами­чески выделенной предыдущим вызовом оператора malloc, используется опе­ратор free(newPtr);

Связанный список - это линейный набор ссылающихся на себя структур, называемых узлами, и объединенных указателем-связкой, отсюда и назва­ние - «связанный» список. Доступ к связанному списку обеспечивается ука­зателем на первый узел списка. Доступ к следующим узлам производится че­рез связывающий указатель, хранящийся в каждом узле. По общему соглаше­нию связывающий указатель в последнем узле списка устанавливается в NULL, отмечая конец списка. Данные хранятся в связанном списке динами­чески - каждый узел создается по мере необходимости. Узел может содер­жать данные любого типа, включая другие структуры.

Две основных функции связанных списков - insert и delete. Функция isEmpty называется предикатной функцией -она никак не меняет список, а всего лишь определяет, является ли список пустым (т.е. указатель на первый узел списка равен NULL). Если список пуст, возвращается значение 1, в про­тивном случае 0. Функция printList распечатывает список. Символы вставляются в список в алфавитном порядке. Функции insert пе­редаются адрес списка и символ, который необходимо вставить. Адрес списка необходим, когда значение должно быть вставлено в начало списка. Передача адреса списка позволяет модифицировать список (т.е. указатель на первый узел списка) через вызов по ссылке. Так как список сам по себе является ука­зателем (на свой первый элемент), при передаче адреса списка создается ука­затель на указатель (другими словами, это двойная косвенная адресация). Функция delete получает адрес указателя на начало списка и символ, ко­торый нужно удалить.

Стек - это упрощенный вариант связанного списка. Новые узлы могут добавляться в стек и удаляться из стека только сверху. Стек часто называют структурой вида последним пришел - первым вышел ( LIFO ). На стек ссылаются через указатель на верхний элемент стека. Связывающий элемент в последнем узле стека установлен равным NULL, чтобы пометить границу стека. Основные функции, используемые при работе со стеками - push и pop. Функция push создает новый узел и помещает его на вершину стека. Функция pop удаляет верхний узел стека, освобождает память, которая была выделена изъятому узлу, и возвращает изъятое значение.

Другой распространенной структурой данных является очередь. В очереди узлы удаляются только с головы, а добавляются только в хвост очереди. По этой причине очереди часто называют структура­ми данных типа первым пришел - первым ушел (FIFO). Операции постановки в очередь и удаления из очереди носят названия enqueue (поставить в очередь) и dequeue (исключить из очереди).

Связанные списки, стеки и очереди - все это примеры линейных струк­тур данных. Дерево же - нелинейная, двумерная структура данных с особы­ми свойствами. Узлы дерева содержат две или более связей.

Первый узел дерева называется корневым. Каждая связь корневого узла ссылается на потомка. Левый потомок - первый узел левого поддерева, а правый потомок - первый узел правого поддерева. Потомки одного узла на­зываются узлами-сиблингами. Узел, не имеющий потомков, называется лис­том. Программисты обычно рисуют деревья от корневого узла вниз - т.е. в направлении, противоположном росту настоящих деревьев. Двоичное дерево поиска (с неповторяющимися зна­чениями в узлах) устроено так, что значения в любом левом поддереве меньше, чем значение в родительском узле, а значения в любом правом поддереве больше, чем значение в родительском узле.

 

Создание класса.

Классы дают возможность программисту моделировать объекты, которые обладают атрибутами (представленными в виде элементов данных) и кото­рым присуще определенное поведение или действие (представленное в виде элементов-функций). При определении типов, содержащих элементы данных и элементы-функции, в C++ обычно используется ключевое слово class. В других объектно-ориентированных языках программирования элемен­ты-функции иногда называют методами и они активируются в ответ на посылае­мые объекту сообщения. Сообщение соответствует вызову элемента-функции.Сразу после определения класса его имя может быть использовано для объявления объектов этого класса. На рис. 1 показано простое определение класса Time.

class Time

{

public: Time(); void setTime(int, int, int); void printMilitary(); void printStandard(); private: int hour; // 0 - 23int minute; // 0 - 59int second; // 0 - 59 }; Рис. 1. Простое определение класса Time.

Определение нашего класса Time начинается с ключевого слова class. Тело определения класса ограничено левой { и правой } фигурными скобка­ми. Определение класса завершается точкой с запятой.

Метки public: и private: называются спецификаторами доступа к элементам. Все элементы данных и элементы-функции, объявленные после спецификатора public: (и до следующего спецификатора доступа) доступны всюду, где про­грамма имеет доступ к какому-либо объекту класса Time. Все элементы дан­ных и элементы-функции, объявленные после спецификатора private: (и до следующего спецификатора доступа) доступны только для функций-элементов класса. Спецификаторы доступа к элементам класса всегда заканчиваются двоеточием (: ) и могут многократно появляться в определении класса.

Определение класса после спецификатора public: содержит прототипы следующих четырех элементов-функций: Time, set Time, print Military и print Standard. Эти функции называют открытыми, или публичными элемента­ми-функциями, а также интерфейсом класса. Эти функции будут использова­ться клиентами (пользователями) класса для манипулирования его данными.

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

Три целочисленных элемента появляются после спецификатора private:. Это означает, что эти элементы данных доступны только для функций-элемен­тов класса - и, как мы увидим далее, для друзей класса. Таким образом, к этим элементам данных могут иметь доступ только четыре функ­ции, прототипы которых появляются в определении класса (а также друзья класса). Обычно элементы данных перечисляются в разделе private: класса, а элементы-функции - в разделе public:. Как мы увидим далее, могут сущест­вовать элементы-функции с доступом private и данные с доступом public:.

Сразу после определения класса он может быть использован в качестве типа в объявлениях, подобных следующим:

Time sunset, // объект типа Time

arrayOfTimes[5], // массив объектов типа Time

*poiterToTime, // указатель на объект типа Time

& dinnerTine = sunset; // ссылка на объект типа Time

Имя класса становится новым спецификатором типа. Может существовать много объектов класса, подобно тому, как может быть много переменных типа int. Программист может по мере необходимости создавать новые типы клас­сов. Это одна из многих причин, благодаря которым C++ является расширяе­мым языком.

Функция с тем же именем, что и класс, с предшествующим символом ти­льды ( ~ ), называется деструктором класса. Деструктор производит заключи­тельную «приборку» каждого объекта класса перед тем, как выделенная для него память будет возвращена системе. Рассматриваемый пример не имеет де­структора. Обратите внимание, что функциям, которые класс предусматривает для внешнего мира, предшествует метка public:. В открытых (публичных) функ­циях реализованы возможности, которые класс предоставляет своим клиен­там. Открытые функции класса называют интерфейсом либо открытым ин­терфейсом класса.

Определение класса содержит объявления его элементов данных и элемен­тов-функций. Объявления элементов-функций представляют собой прототипы функций. Элементы-функции могут быть определены внутри класса, однако хорошим стилем является опре­деление функций вне определения класса.

Любая функция-элемент класса может быть определена непо­средственно в его теле (вместо включения туда ее прототипа) или же она мо­жет быть определена после тела класса. Когда элемент-функция определяется после соответствующего определения класса, имени функции предшествуют имя класса и двухместная операция разрешения области действия (:: ). Поско­льку различные классы могут иметь одинаковые имена элементов, операция разрешения области действия «привязывает» имя элемента к имени класса, однозначно идентифицируя функции-элементы данного класса.

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

Можно определять элементы-функции и в теле определения класса. Функ­ции размером более одной или двух строк обычно определяются вне тела опре­деления класса - это помогает отделить интерфейс класса от его реализации. Если функция-элемент определена в определении класса, то она автоматиче­ски становится встроенной. Элементы-функции, определенные вне определе­ния класса, можно сделать встроенными путем явного использования ключе­вого слова inline. He забывайте, что компилятор оставляет за собой право не делать встроенной любую функцию.

Классы упрощают программирование, поскольку клиент (или пользова­тель объекта класса) должен иметь дело только с инкапсулированными, т. е. встроенными в объект, действиями. При разработке такого рода действий обычно ориентируются на клиентов класса, а не на реализацию. Клиенты не должны иметь дела с реализацией класса. Интерфейсы тоже изменяются, но гораздо реже, чем реализации. При изменении реализации соответствующим образом должен измениться зависящий от реализации код. Скрывая реализа­цию, мы устраняем возможность зависимости других частей программы от де­талей реализации класса.

Часто нет необходимости создавать классы «с нуля». Напротив, классы могут производиться от других классов, предоставляющих программисту дей­ствия, которые могут использовать новые классы. Классы также могут вклю­чать в качестве элементов объекты других классов. Такого рода повторное ис­пользование программного обеспечения может значительно повысить произво­дительность труда программиста. Создание производных классов на основе су­ществующих называется наследованием. Включение классов в качестве элементов других классов называется компози­цией

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


Поделиться:



Популярное:

  1. Глава 15 «Передача» бренда сотрудникам
  2. Глава 16 «Передача» бренда потребителям
  3. Глава 18. Влияние среды: передача чувств и подражание
  4. ГЛАВА 20 ПЕРЕДАЧА И СОБИРАНИЕ СВЕТА
  5. Глава 55. ПЕРЕДАЧА ЛИЦА, ОСУЖДЕННОГО К ЛИШЕНИЮ
  6. КАК ОСУЩЕСТВЛЯЕТСЯ ТЕЛЕВИЗИОННАЯ ПЕРЕДАЧА
  7. Классификация карт по назначению и другим параметрам.
  8. Классификация условий и характера труда на рабочем месте по параметрам микроклимата и световой среды
  9. Корреспонденция («передача»)
  10. Массивы (Объявление массивов, Передача массивов в функции, Сортировка массивов, Многомерные массивы)
  11. Невозможен процесс, единственным результатом которого была бы передача энергии путем теплообмена от тела с низкой температурой к телу с более высокой температурой.
  12. Общие принципы построения современных ЭВМ. Процессоры, их развитие и сравнительная характеристика. Шины. Последовательная и параллельная передача данных.


Последнее изменение этой страницы: 2016-04-10; Просмотров: 631; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.045 с.)
Главная | Случайная страница | Обратная связь