Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
109
Добавлен:
17.05.2015
Размер:
519.68 Кб
Скачать

Указатели

Когда компилятор обрабатывает оператор определения переменной, например, int i=10, он выделяет память в соответствии с типом (int) и инициализирует ее значение (i=10).

Все обращения в программе к переменной по ее имени (i) заменяются компилятором на адрес области памяти, в которой хранится значение переменной. Программист сам задает переменные для хранения адресов областей памяти. Такие переменные называются указателями. Указатели предназначены для хранения адресов областей памяти.

В языках С/С++ выделяют три типа указателей:

указатель на объект; указатель на функцию; указатель на void.

  • Указатель на функцию – содержит адрес в сегменте кода, по которому располагается исполняемый код функции, иными словами содержит адрес по которому передается управление при вызове функции. Указатели на функцию используются для косвенного вызова функции (не через ее имя, а через обращение к переменной, хранящей ее адрес), а также для передачи имени функции в другую функцию в качестве параметра.

Формат указателя на функцию имеет следующий вид:

тип (*имя) (список_типов_аргументов);

Указатель функции имеет тип «указатель функции, возвращающей значение заданного типаи имеющей аргументы еакже заданного типа».

Пример 1: int (*fun) (double, double);

такой указатель задает указатель с именем fun на функцию (имеющую или), возвращающую значение типа int и имеющую два аргумента типа double.

Указатели на функцию широко используются при перезагрузки функции (перезагрузка – вызов одной и той функции с разными параметрами).

  • Указатель на объект – содержит адрес области памяти, в которой хранятся данные определенного типа (основного или составного).

Простейший формат указателя на объект имеет вид:

тип *имя;

где тип может быть любым, кроме ссылки и битового поля, причем тип может быть к этому моменту только объявлен, но еще не определен. Например, в структуре может присутствовать указатель на структуру того же типа.

Пример 2: int *a, b, *c;

такая запись описывает для указателя на целое а и с и целую переменную b.

Как видно из примера * относится непосредственно к имени, поэтому для того, чтобы объявить несколько указателей требуется ставить ее перед именем каждой переменной.

  • Указатель на void – применяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определен (например, когда в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов).

Указателю на void можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо действий с областью памяти требуется произвести явное преобразование типов конкретному типу. Указатель может быть константой или переменной, а также указывать на константу или переменную.

Пример 3:

int i; // определяется целая переменная

const int ci=1; // определяется целочисленная константа

int *pi; //указатель на целую переменную

const int *pci; //указатель на целую константу

int *const cp=&i; //указатель-константа на целую переменную

const int *const cpc=&ci; // указатель-константа на целую константу

Как видно из примера, модификатор const, находящийся между именем указателя и *, относится к самому указателю, и запрещает его изменение, а модификатор const перед * задает постоянство значения, на которое он указывает.

Для инициализации указателей использована операция получения адреса &. Величины типа указатель подчиняются общим правилам определения области действия, времени жизни и видимости.

Инициализация указателейУказатели чаще всего используют при работе с динамической памятью. Эта свободная часть оперативной памяти, в которой можно во время выполнения программы выделить место в соответствии с нашими потребностями. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, осуществляется при помощи указателей. Время жизни динамических переменных определяется от точки создания до конца программы или до явного освобождения памяти. В языке С++ используется два способа работы с динамической памятью:

  1. использует семейства функций malloc наследство от Си;

  2. использует операции new и delete.

Распространенный источник ошибок в программах – непреднамеренное использование неинициализированных указателей. Поэтому, при определении указателя необходимо стремиться выполнить его инициализацию, т.е. присвоить некоторые начальные значения. Инициализатор записывается после имени указателя либо в круглых скобках, либо после знака равенства.

Существуют следующие способы инициализации указателя:

  1. Присвоение указателю адрес существующего объекта:

а) & - с помощью операции получения адреса:

int a=5; //целая переменная

int *p=&a; //в указатель записывается адрес а

int *p(&a); //то же самое другим способом

б) с помощью имени массива или функции, которые требуются как адрес:

int b[10]; //задается массив (целочисленный)

int *t=b; //присваивается адрес начального массива

void f (int a) {/*…*/}; //определение функции

void (*pf) (int); //указатель на функцию

pf=f; //присвоение адреса функции

в) с помощью значения другого инициализированного указателя:

int a=5; int *p(&a);

int *r=p;

  1. Присвоение указателю адреса области памяти в явном виде:

char *vp=(char *)

0xB8000000;

В этой записи 0xB8000000 – 16-ричная константа, (char *) – операция приведения типа: const преобразуется к типу «указатель на char ».

  1. Присвоение пустого значения:

  1. int *null=NULL; //происходит присвоение нулевого адреса указателя NULL при const null, определенный в некоторых заголовочных файлах в языках С

  2. int *result=0; //присваивает указателю явное значение 0

Рекомендуется использовать второй запись, т.к. это значение типа int будет правильно преобразовано стандартными способами в соответствии с контекстом. Поскольку гарантируется, что объектов с нулевым адресом нет, пустой указатель можно использовать для проверки, ссылается указатель на конкретный объект или нет.

  1. Выделение участка динамической памяти и присвоение его адреса указателю:

  1. при помощи операции new:

int *n=new int; //1

int *m=new int(10); //2

int *q=new int[10]; //3

б) с помощью функции malloc:

int *u=(int*) malloc (sizeof (int)); //4

97-100

Объектно-ориентированное программирование (ООП) — это технология создания сложного программного обеспечения, которое основано на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию с наследованием свойств.

Основное достоинство ООП — это сокращение количества межмодульных вызовов и уменьшение объёмов информации передаваемой между модулями. Это достигается за счет более полной локализации данных и интегрирования их с подпрограммами обработки.

Основные недостатки в ООП — это некоторое снижение быстродействия из-за более сложной организации программной системы, а также, как правило, заметное увеличение объёма бинарного кода (особенно при использовании стандартных библиотек классов в небольших программах) из-за того, что большинство современных компиляторов и компоновщиков не способны выявить и удалить весь код, приходящийся на неиспользуемые классы, виртуальные методы и другие элементы ООП.

В Object Pascal классы задаются с помощью типа object, аналогичного record, который кроме полей данных может содержать заголовки процедур и функций (они называютсяметодами). Имена описываемых методов следуют за именем класса через точку.

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

Это важный инструмент ООП наряду с полиморфизмомНаследованием и Абстракцией данных.

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

Delphi

В Delphi для создания скрытых полей или методов их достаточно объявить в секции private.

TMyClass = class

private

FMyField: Integer;

procedure SetMyField(const Value: Integer);

function GetMyField: Integer;

public

property MyField: Integer read GetMyField write SetMyField;

end;

Для создания интерфейса доступа к скрытым полям в Delphi введены свойства.

C++

class A

{

public:

int a, b; //данные открытого интерфейса

int ReturnSomething(); //метод открытого интерфейса

private:

int Aa, Ab; //скрытые данные

void DoSomething(); //скрытый метод

};

Класс А инкапсулирует свойства Aa, Ab и метод DoSomething, представляя внешний интерфейс ReturnSomething, a, b.

Наследование  механизм языка позволяющий написать новый класс на основе уже существующего(родительского, базового) класса. Класс–потомок может добавить собственные методы и свойства и пользоваться родительскими методами и свойствами. Позволяет строить иерархии классов.

Это важный инструмент ООП наряду с ИнкапсуляциейПолиморфизмом и Абстракцией данных.

Delphi (Object Pascal)

Для использования механизма наследования в Delphi необходимо в объявлении класса справа от слова class указать класс предок:

Предок:

TAncestor = class

private

protected

public

// Виртуальная процедура

procedure VirtualProcedure; virtual; abstract;

procedure StaticProcedure;

end;

Наследник:

TDescendant = class(TAncestor)

private

protected

public

// Перекрытие виртуальной процедуры

procedure VirtualProcedure; override;

procedure StaticProcedure;

end;

Абсолютно все классы в Delphi являются потомками класса TObject. Если класс-предок не указан, то подразумевается, что новый класс является прямым потомком класса TObject.

Множественное наследование в Delphi частично поддерживается за счёт использования классов-помощников (Сlass Helpers).

Соседние файлы в папке шпоры