Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 163 164 165 166 167 168 169 170 171 ... 337
class="code">age нумеруются от 0 до age.size()–1. Вектор age можно изобразить следующим образом:

Как реализовать эту схему в компьютерной памяти? Как хранить значения и обеспечивать к ним доступ? Очевидно, что мы должны определить класс и назвать его vector. Далее, нужен один член класса для хранения размера вектора и еще один член для хранения его элементов. Как же представить множество элементов, количество которых может изменяться? Для этого можно было бы использовать стандартный класс vector, но в данном контексте это было бы мошенничеством: мы же как раз этот класс и разрабатываем.

Итак, как представить стрелку, изображенную на рисунке? Представим себе, что ее нет. Мы можем определить структуру данных фиксированного размера.

class vector {

  int size,age0,age1,age2,age3;

  // ...

};

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

Это просто и красиво, но как только мы попробуем добавить элемент с помощью функции push_back(), окажемся в затруднительном положении: мы не можем добавить элемент, так как количество элементов зафиксировано и равно четырем. Нам нужно нечто большее, чем структура данных, хранящая фиксированное количество элементов. Операции, изменяющие количество элементов в объекте класса vector, такие как push_back(), невозможно реализовать, если в классе vector количество элементов фиксировано. По существу, нам нужен член класса, ссылающийся на множество элементов так, чтобы при расширении памяти он мог ссылаться на другое множество элементов. Нам нужен адрес первого элемента. В языке C++ тип данных, способный хранить адрес, называют указателем (pointer). Синтаксически он выделяется суффиксом *, так что double* означает указатель на объект типа double. Теперь можем определить первый вариант класса vector.

// очень упрощенный вектор элементов типа double (вроде vector<double>)

class vector {

  int sz;        // размер

  double* elem;  // указатель на первый элемент (типа double)

public:

  vector(int s); // конструктор: размещает в памяти s чисел

  // типа double,

  // устанавливает на них указатель elem,

  // хранит число s в члене sz

  int size() const { return sz; } // текущий размер

};

Прежде чем продолжить проектирование класса vector, изучим понятие “указатель” более подробно. Понятие “указатель” — вместе с тесно связанным с ним понятием “массив” — это ключ к понятию “память” в языке C++.

17.3. Память, адреса и указатели

 

 Память компьютера — это последовательность байтов. Эти байты нумеруются от нуля до последнего. Адресом (address) называют число, идентифицирующее ячейку в памяти. Адрес можно считать разновидностью целых чисел. Первый байт памяти имеет адрес 0, второй — 1 и т.д. Мегабайты памяти можно визуализировать следующим образом:

Все, что расположено в памяти, имеет адрес. Рассмотрим пример.

int var = 17;

Эта инструкция резервирует участок памяти, размер которого определяется размером типа int, для хранения переменной var и записывает туда число 17. Кроме того, можно хранить адреса и применять к ним операции. Объект, хранящий адрес, называют указателем. Например, тип, необходимый для хранения объекта типа int, называется указателем на int и обозначается как int*.

int* ptr = &var; // указатель ptr хранит адрес переменной var

Для определения адреса объекта используется оператор взятия адреса, унарный &. Итак, если переменная var хранится в участке памяти, первая ячейка которого имеет адрес 4096 (или 212), то указатель ptr будет хранить число 4096.

По существу, память компьютера можно рассматривать как последовательность байтов, пронумерованную от 0 до size-1. Для некоторых машин такое утверждение носит слишком упрощенный характер, но для нашей модели этого пока достаточно.

Каждый тип имеет соответствующий тип указателя. Рассмотрим пример.

char ch = 'c';

char* pc = &ch; // указатель на char

int ii = 17;

int* pi = &ii; // указатель на int

Если мы хотим увидеть значение объекта, на который ссылаемся, то можем применить к указателю оператор разыменования, унарный *. Рассмотрим пример.

cout << "pc==" << pc << "; содержимое pc==" << *pc << "n";

cout << "pi==" << pi << "; содержимое pi==" << *pi << "n";

Значением *pc является символ c, а значением *pi — целое число 17. Значения переменных pc и pi зависят от того, как компилятор размещает переменные ch и ii в памяти. Обозначение, используемое для значения указателя (адрес), также может изменяться в зависимости от того, какие соглашения приняты в системе; для обозначения значений указателей часто используются шестнадцатеричные числа (раздел A.2.1.1).

Оператор разыменования также может стоять в левой части оператора присваивания.

*pc = 'x'; // OK: переменной char, на которую ссылается

           // указатель pc,

           // можно присвоить символ 'x'

*pi = 27;  // OK: указатель int* ссылается на int, поэтому *pi —

           // это int

*pi = *pc; // OK: символ (*pc) можно присвоить переменной

           // типа int (*pi)

 

 Обратите внимание: несмотря на то, что значение указателя является целым числом, сам указатель целым числом не является. “На что ссылается int?” — некорректный вопрос. Ссылаются не целые числа, а указатели. Тип указателя позволяет выполнять операции над адресами, в то время как тип int позволяет выполнять (арифметические и логические) операции над целыми числами. Итак, указатели и целые числа нельзя смешивать.

int i = pi; // ошибка: нельзя присвоить объект типа int*

            // объекту типа int

pi = 7;     // ошибка: нельзя присвоить объект типа int объекту

            // типа int*

Аналогично, указатель на char (т.е. char*) — это не указатель на int (т.е. int*). Рассмотрим пример.

pc = pi; // ошибка: нельзя присвоить объект типа int*

         // объекту типа char*

pi = pc; // ошибка: нельзя присвоить объект типа char*

         // объекту

1 ... 163 164 165 166 167 168 169 170 171 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий