Стенли Липпман - Язык программирования C++. Пятое издание
- Название:Язык программирования C++. Пятое издание
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс
- Год:2014
- Город:Москва
- ISBN:978-5-8459-1839-0
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стенли Липпман - Язык программирования C++. Пятое издание краткое содержание
Вы держите в руках новое издание популярного и исчерпывающего бестселлера по языку программирования С++, которое было полностью пересмотрено и обновлено под
. Оно поможет вам быстро изучить язык и использовать его весьма эффективными и передовыми способами. В соответствии с самыми передовыми и современными методиками изложения материала авторы демонстрируют использование базового языка и его стандартной библиотеки для разработки эффективного, читабельного и мощного кода.
С самого начала этой книги читатель знакомится со стандартной библиотекой С++, ее самыми популярными функциями и средствами, что позволяет сразу же приступить к написанию полезных программ, еще не овладев всеми нюансами языка. Большинство примеров из книги было пересмотрено так, чтобы использовать новые средства языка и продемонстрировать их наилучшие способы применения. Эта книга — не только проверенное руководство для новичков в С++, она содержит также авторитетное обсуждение базовых концепций и методик языка С++ и является ценным ресурсом для опытных программистов, особенно желающих побыстрей узнать об усовершенствованиях С++11.
Стенли Б. Липпман Жози Лажойе Барбара Э. Му • Узнайте, как использовать новые средства языка С++11 и стандартной библиотеки для быстрого создания надежных программ, а также ознакомьтесь с высокоуровневым программированием
• Учитесь на примерах, в которых показаны передовые стили программирования и методики проектирования
• Изучите принципы и узнайте почему язык С++11 работает именно так
• Воспользуйтесь множеством перекрестных ссылок, способных помочь вам объединить взаимосвязанные концепции и проникнуть в суть
• Ознакомьтесь с современными методиками обучения и извлеките пользу из упражнений, в которых подчеркиваются ключевые моменты, позволяющие избежать проблем
• Освойте лучшие методики программирования и закрепите на практике изученный материал
Исходный код примеров можно загрузить с веб-страницы книги на сайте издательства по адресу: http://www.williamspublishing.com
Язык программирования C++. Пятое издание - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Обычно классы, управляющие ресурсами, расположенными вне его, должны определять функции-члены управления копированием. Как упоминалось в разделе 13.6, такие классы нуждаются в деструкторах, освобождающих зарезервированные объектом ресурсы. Если класс нуждается в деструкторе, он почти наверняка нуждается также в конструкторе копий и операторе присвоения копии.
Чтобы определить эти функции-члены, сначала следует решить, что будет означать копирование объекта данного типа. Вообще, есть два способа: операцию копирования можно определить так, чтобы класс вел себя, как значение или как указатель.
У классов, которые ведут себя, как значения, есть собственное состояние. При копировании объекта как значения копия и оригинал независимы друг от друга. Внесенные в копию изменения никак не влияют на оригинал, и наоборот.
Классы, действующие как указатели, используют состояние совместно. При копировании объектов таких классов копии и оригиналы используют те же данные. Изменения, внесенные в копии, изменяют также оригинал, и наоборот.
Из использованных ранее библиотечных классов поведением, подобным значениям, обладали классы библиотечных контейнеров и класс string. Ничего удивительного, что класс shared_ptrдемонстрирует поведение, подобное указателю, как и класс StrBlob(см. раздел 12.1.1). Типы ввода-вывода и класс unique_ptrне допускают ни копирования, ни присвоения, поэтому их поведение не похоже ни на значение, ни на указатель.
Чтобы проиллюстрировать эти два подхода, определим для используемого в упражнениях класса HasPtrфункции-члены управления копированием. Сначала заставим класс действовать, как значение, а затем повторно реализуем его в версии, ведущей себя, как указатель.
У класса HasPtrесть два члена типа intи указатель на тип string. Обычно классы непосредственно копируют переменные-члены встроенного типа (кроме указателей); такие члены являются значениями, а следовательно, ведут себя обычно, как значения. Происходящее при копировании указателя-члена определяет то, должно ли у такого класса, как HasPtr, быть поведение, подобное значению или указателю.
Упражнение 13.22. Предположим, класс HasPtrдолжен вести себя, как значение. Таким образом, у каждого его объекта должна быть собственная копия строки, на которую указывает объект. Определения функций-членов управления копированием рассматривается в следующем разделе, но уже сейчас известно все необходимое для их реализации. Напишите конструктор копий класса HasPtrи оператор присвоения копии прежде, чем продолжите чтение.
13.2.1. Классы, действующие как значения
Для обеспечения поведения, подобного значению, у каждого объекта должна быть собственная копия ресурса, которым управляет класс. Это значит, что у каждого объекта класса HasPtrдолжна быть собственная копия строки, на которую указывает указатель ps. Для реализации поведения, подобного значению, классу HasPtrнужно следующее.
• Конструктор копий, который копирует строку, а не только указатель.
• Деструктор, освобождающий строку.
• Оператор присвоения копии, освобождающий строку существующего объекта и копирующий ее значение в строку правого операнда.
Вот подобная значению версия класса HasPtr:
class HasPtr {
public:
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) { }
// у каждого объекта класса HasPtr есть собственный экземпляр строки,
// на которую указывает указатель ps
HasPtr(const HasPtr &p) :
ps(new std::string(*p.ps)), i(p.i) { }
HasPtr& operator=(const HasPtr &);
~HasPtr() { delete ps; }
private:
std::string *ps;
int i;
};
Класс достаточно прост, все, кроме оператора присвоения, определено в теле класса. Первый конструктор получает (необязательный) аргумент типа string. Он динамически резервирует собственную копию этой строки и сохраняет ее адрес в указателе ps. Конструктор копий также резервирует собственный экземпляр строки. Деструктор освобождает память, зарезервированную ее конструкторами, выполняя оператор deleteдля указателя-члена ps.
Обычно операторы присвоения объединяют действия деструктора и конструктора копий. Подобно деструктору, оператор присвоения освобождает ресурсы левого операнда. Подобно конструктору копий, оператор присвоения копирует данные из правого операнда. Однако критически важно, чтобы эти действия осуществлялись в правильной последовательности, даже если объект присваивается сам себе. Кроме того, по возможности следует писать собственные операторы присвоения так, чтобы они оставляли левый операнд в корректном состоянии, иначе произойдет исключение (см. раздел 5.6.2).
В данном случае можно отработать случай присвоения самому себе (и сделать код устойчивым к исключению), осуществляя сначала копирование правого операнда. После копирования освобождается левый операнд и указатель модифицируется так, чтобы он указывал на вновь зарезервированную строку:
HasPtr& HasPtr::operator=(const HasPtr &rhs) {
auto newp = new string(*rhs.ps); // скопировать строку
delete ps; // освободить прежнюю память
ps = newp; // копировать данные из rhs в этот объект
i = rhs.i;
return *this; // возвратить этот объект
};
В этом операторе присвоения, безусловно, сначала выполняется работа конструктора: инициализатор newpидентичен инициализатору ps в конструкторе копий класса HasPtr. Затем, как в деструкторе, удаляется строка, на которую в настоящее время указывает указатель ps. Остается только скопировать указатель на недавно созданную строку и значение типа intиз rhsв этот объект.
Создавая оператор присвоения, следует учитывать два момента.
• Операторы присвоения должны работать правильно, если объект присваивается сам себе.
• Большинство операторов присвоения делят работу с деструктором и конструктором копий.
Шаблон разработки оператора присвоения подразумевает сначала копирование правого операнда в локальный временный объект. После копирования вполне безопасно удалить существующие члены левого операнда. Как только левый операнд будет освобожден, копировать данные из временного объекта в переменные-члены левого операнда.
Для иллюстрации важности принятия мер против присвоения самому себе рассмотрим, что случилось бы, выгляди оператор присвоения так:
Читать дальшеИнтервал:
Закладка: