Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Тут можно читать онлайн Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - бесплатно ознакомительный отрывок. Жанр: comp-programming, издательство Литагент «ДМК»233a80b4-1212-102e-b479-a360f6b39df7, год 2006. Здесь Вы можете читать ознакомительный отрывок из книги онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
  • Автор:
  • Жанр:
  • Издательство:
    Литагент «ДМК»233a80b4-1212-102e-b479-a360f6b39df7
  • Год:
    2006
  • Город:
    Москва
  • ISBN:
    5-94074-304-8
  • Рейтинг:
    3.6/5. Голосов: 101
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5

Скотт Майерс - Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ краткое содержание

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - описание и краткое содержание, автор Скотт Майерс, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru

Эта книга представляет собой перевод третьего издания американского бестселлера Effective C++ и является руководством по грамотному использованию языка C++. Она поможет сделать ваши программы более понятными, простыми в сопровождении и эффективными. Помимо материала, описывающего общую стратегию проектирования, книга включает в себя главы по программированию с применением шаблонов и по управлению ресурсами, а также множество советов, которые позволят усовершенствовать ваши программы и сделать работу более интересной и творческой. Книга также включает новый материал по принципам обработки исключений, паттернам проектирования и библиотечным средствам.

Издание ориентировано на программистов, знакомых с основами C++ и имеющих навыки его практического применения.

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - читать онлайн бесплатно ознакомительный отрывок

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - читать книгу онлайн бесплатно (ознакомительный отрывок), автор Скотт Майерс
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Если вы пользуетесь только обычными формами new и delete, то исполняющая система легко найдет тот вариант delete, который знает, как отменить действие, выполненное оператором new. Проблема поиска правильного варианта delete возникает тогда, когда вы объявляете необычные формы оператора new – такие, которые принимают дополнительные параметры.

Например, предположим, что вы написали оператор new уровня класса, который требует задания потока ofstream, куда должна выводиться отладочная информация о выделении памяти, и вместе с ним написали также обычный оператор delete уровня класса:

class Widget {

public:

...

static void *operator new(std:size_t size, // необычная

std::ostream& logStream) // форма new

throw(std::bad_alloc);

static void operator delete(void *pMemory, // обычная

std:size_t size) throw(); // форма delete

// уровня класса

...

};

Такое решение наверняка приведет к ошибкам, но чтобы понять, почему это так, придется познакомиться с некоторыми терминами.

Функция operator new, принимающая дополнительные параметры (помимо обязательного аргумента size_t), называется оператором new с размещением или размещающим оператором new (placement new). Приведенный выше оператор new как раз и является таковым. Особенно полезным бывает размещающий оператор new, для которого вторым аргументом служит указатель на область памяти, где объект должен быть сконструирован. Этот оператор new выглядит так:

void *operator new(std::size_t, void *pMemory) throw(); // “размещающий new”

Эта версия new является частью стандартной библиотеки C++, и вы получаете к ней доступ, включая в исходный текст директиву #include . Кстати говоря, такой оператор new используется в реализации класса vector для создания объектов в выделенной для вектора памяти. Это также первоначальная версия оператора new с размещением; именно она и получила название «placement new». Таким образом, сам термин «размещающий new» перегружен. Обычно, когда говорят о размещающем new, имеют в виду эту конкретную функцию: оператор new, принимающий дополнительный аргумент типа void*. Реже так говорят о любой другой версии new, принимающей дополнительные аргументы. Обычно контекст исключает противоречивые толкования, но важно понимать, что общий термин «размещающий new» означает любую версию new, принимающую дополнительные аргументы, поскольку выражение «размещающий delete» или «delete с размещением» (которое мы сейчас обсудим) происходит от него.

Но вернемся к объявлению класса Widget, которое я не одобрил. Проблема в том, что этот класс открывает возможность утечки памяти. Рассмотрим следующий пользовательский код, который протоколирует информацию о выделении памяти в поток cerr при динамическом создании объектов Widget:

Widget *pw = new (std::cerr) Widget; // вызвать оператор new, передав cerr

// в качестве параметра типа ofstream;

// это ведет к утечке памяти в случае,

// когда конструктор Widget возбуждает

// исключение

Если выделение памяти прошло успешно, но конструктор Widget возбуждает исключение, то исполняющая система отвечает за освобождение той памяти, которую успел выделить оператор new. Исполняющая система понятия не имеет, как работает вызванная версия оператора new, поэтому не может отменить результат операции самостоятельно. Вместо этого исполняющая система ищет версию оператора delete, которая принимает то же количество аргументов того же типа, что и new, и если находит его, то вызывает. В данном случае оператор new принимает дополнительный аргумент типа ostream&, поэтому соответствующий оператор delete должен иметь следующую сигнатуру:

void operator delete(void *, std::ostream&) throw();

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

Правило простое: если оператору new с дополнительными аргументами не соответствует оператор delete с такими же аргументами, то никакой delete не вызывается в случае необходимости отменить выделение памяти, выполненное new. Чтобы избежать утечек памяти в приведенном выше коде, Widget должен объявить размещающий оператор delete, который соответствует размещающему оператору new, который выполняет протоколирование:

class Widget {

public:

...

static void *operator new(std:size_t size, std::ostream& logStream)

throw(std::bad_alloc);

static void operator delete(void *pMemory) throw();

static void operator delete(void *pMemory, std::ostream& logStream)

throw();

...

};

С этим изменением, если конструктор Widget возбудит исключение в предложении

Widget *pw = new (std::cerr) Widget; // как раньше, но теперь никаких

// утечек

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

Посмотрим, что произойдет, если никаких исключений нет (как обычно и бывает), а в пользовательском коде присутствует явный вызов delete:

delete pw; // вызов обычного оператора delete

Как сказано в комментарии, здесь вызывается обычный оператор delete, а не размещающая версия. Размещающий delete вызывается, только если возбуждает исключение конструктор, следующий за вызовом размещающего new. Если delete применяется к указателю (в примере выше – pw), то версия delete с размещением никогда не будет вызвана.

Это значит, что для предотвращения всех утечек памяти, ассоциированных с размещающей версией new, вы должны также предоставить и обычный оператор delete (на случай, если в конструкторе не возникнет исключений), и размещающую версию с теми же дополнительными аргументами, что и у размещающего new (если таковой имеется). Поступайте так, и вы никогда не потеряете сон из-за неуловимых утечек памяти. Ну, по крайней мере, из-за утечек памяти по этой причине.

Кстати, поскольку имена функций-членов скрывают одноименные функции в объемлющих контекстах (см. правило 33), вы должны быть осторожны, чтобы избежать того, что операторы new уровня класса скроют другие версии new (в том числе обычные), на которые рассчитывают пользователи. Например, если у вас есть базовый класс, в котором объявлена только размещающая версия оператора new, пользователи обнаружат, что обычная форма new стала недоступной:

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Скотт Майерс читать все книги автора по порядку

Скотт Майерс - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ отзывы


Отзывы читателей о книге Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ, автор: Скотт Майерс. Читайте комментарии и мнения людей о произведении.


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x