Введение в язык Си++

Указатели


Для большинства типов T T* является типом указатель на T. То есть, в переменной типа T* может храниться адрес объекта типа T. Для указателей на вектора и указателей на функции вам, к сожалению, придется пользоваться более сложной записью:

int* pi; char** cpp; // указатель на указатель на char int (*vp)[10]; // указатель на вектор из 10 int'ов int (*fp)(char, char*); // указатель на функцию // получающую параметры (char, char*) // и возвращающую int

Основная операция над указателем - разыменование, то есть ссылка на объект, на который указывает указатель. Эта операция также называется косвенным обращением. Операция разыменования - это унарное * (префиксное). Например:

char c1 = 'a'; char* p = c1 // в p хранится адрес c1 char c2 = *p; // c2 = 'a'

Переменная, на которую указывает p,- это c1, а значение, которое хранится в c1, это 'a', поэтому присваиваемое c2 значение *p есть 'a'.

Над указателями можно осуществлять некоторые арифметические действия. Вот, например, функция, подсчитывающая число символов в строке (не считая завершающего 0):

int strlen(char* p) { int i = 0; while (*p++) i++; return i; }

Другой способ найти длину состоит в том, чтобы сначала найти конец строки, а затем вычесть адрес начала строки из адреса ее конца:

int strlen(char* p) { char* q = p; while (*q++) ; return q-p-1; }

Очень полезными могут оказаться указатели на функции; они обсуждаются в


Если производный класс derived имеет открытый базовый класс base, то указатель на derived можно присваивать переменной типа указатель на base не используя явное преобразование типа. Обратное преобразование, указателя на base в указатель на derived, должно быть явным. Например:

class base { /* ... */ }; class derived : public base { /* ... */ };

derived m; base* pb = m // неявное преобразование derived* pd = pb; // ошибка: base* не является derived* pd = (derived*)pb; // явное преобразование

Иначе говоря, объект производного класса при работе с ним через указатель и можно рассматривать как объект его базового класса. Обратное неверно.

Будь base закрытым базовым классом класса derived, неявное преобразование derived* в base* не делалось бы. Неявное преобразование не может в этом случае быть выполнено, потому что к открытому члкну класса base можно обращаться через указатель на base, но нельзя через указатель на derived:

class base { int m1; public: int m2; // m2 - открытый член base };

class derived : base { // m2 НЕ открытый член derived };

derived d; d.m2 = 2; // ошибка: m2 из закрытой части класса base* pb = d // ошибка: (закрытый base) pb-m2 = 2; // ok pb = (base*)d // ok: явное преобразование pb-m2 = 2; // ok

Помимо всего прочего, этот пример показывает, что используя явное приведение к типу можно сломать правила защиты. Ясно, делать это не рекомендуется, и это приносит программисту заслуженную "награду". К несчастью , недисциплинированное использование явного преобразования может создать адские условия для невинных жертв, которые эксплуатируют программу, где это делается. Но, к счастью, нет способа воспользоваться приведением для получения доступа к закрытому имени m1. Закрытый член класса может использоваться только членами и друзьями этого класса.



Содержание раздела