Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

Глава 18

Векторы и массивы

“Покупатель, будь бдителен!”

Полезный совет

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

18.1. Введение

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

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

• Объект в памяти имеет фиксированный размер.

• Объект в памяти занимает конкретное место.

• Компьютер предоставляет только самые необходимые операции над объектами (например, копирование слова, сложение двух слов и т.д.).

По существу, эти ограничения относятся к встроенным типам и операциям языка С++ (и унаследованы от языка С; см. раздел 22.2.5 и главу 27). В главе 17 мы уже ознакомились с типом

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

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

Мы обязательно обсудим проблему манипуляции памятью без помощи высокоуровневых типов, таких как

vector
и
string
, изучим массивы и указатели, их взаимосвязь и способы применения, а также ловушки, связанные с их использованием. Это важная информация для любого программиста, вынужденного работать с низкоуровневыми кодами, написанными на языке C++ или C.

Отметим, что детали класса

vector
характерны не только для векторов, но и для других высокоуровневых типов, которые создаются из низкоуровневых. Однако каждый высокоуровневый тип (
string
,
vector
,
list
,
map
и др.) в любом языке создается из одинаковых машинных примитивов и отражает разнообразие решений фундаментальных проблем, описанных в этой главе.

18.2. Копирование

Рассмотрим класс

vector
в том виде, в каком он был представлен в конце главы 17.

class vector {

int sz; // размер

double* elem; // указатель на элементы

public:

vector(int s) // конструктор

:sz(s), elem(new double[s]) { /* */ } // выделяет

// память

~vector // деструктор

{ delete[ ] elem; } // освобождает

// память

// ...

};

Попробуем скопировать один из таких векторов.

void f(int n)

{

vector v(3); // определяем вектор из трех элементов

v.set(2,2.2); // устанавливаем v[2] равным 2.2

vector v2 = v; // что здесь происходит?

// ...

}

Теоретически объект

v2
должен стать копией объекта
v
(т.е. оператор = создает копии); иначе говоря, для всех
i
в диапазоне
[0:v.size]
должны выполняться условия
v2.size==v.size
и
v2[i]==v[i]
. Более того, при выходе из функции
f
вся память возвращается в свободный пул. Именно это (разумеется) делает класс
vector
из стандартной библиотеки, но не наш слишком простой класс
vector
. Наша цель — улучшить наш класс
vector
, чтобы правильно решать такие задачи, но сначала попытаемся понять, как на самом деле работает наша текущая версия. Что именно она делает неправильно, как и почему? Поняв это, мы сможем устранить проблему. Еще более важно то, что мы можем распознать аналогичные проблемы, которые могут возникнуть в других ситуациях.

По умолчанию копирование относительно класса означает “скопировать все данные-члены”. Это часто имеет смысл. Например, мы копируем объект класса

Point
, копируя его координаты. Однако при копировании членов класса, являющихся указателями, возникают проблемы. В частности, для векторов в нашем примере выполняются условия
v.sz==v2.sz
и
v.elem==v2.elem
, так что наши векторы выглядят следующим образом:

Иначе говоря, объект

v2
не содержит копии элементов объекта
v
; он ими владеет совместно с объектом
v
. Мы могли бы написать следующий код:

  • Читать дальше
  • 1
  • ...
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: