Компьютерный форум
Правила
Вернуться   Компьютерный форум > Форум программистов > Языки программирования > FAQ > Библиотека описаний > Языки программирования
Перезагрузить страницу Умные указатели в C++
Ответ
 
Опции темы Опции просмотра
  (#1 (permalink)) Старый
Garik Garik вне форума
Member
 
Сообщений: 6,201
Сказал(а) спасибо: 0
Поблагодарили 1 раз в 1 сообщении
Регистрация: 07.06.2002
По умолчанию 21.04.2008, 13:45

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

В этой статье приводится обзор инструментов, позволяющих избежать части проблем, связанных с использованием динамической памяти в C++. Описанные ниже инструменты и методики не являются панацеей от проблем управления памятью, они всего лишь способны облегчить жизнь программисту при условии правильного их использования. Эти инструменты носят общее название «умные указатели» («smart pointers»), что подразумевает их семантическое сходство с обыкновенными указателями C++. Это сходство обеспечивается перегрузкой операторов *, ->, &, и в некоторых случаях оператора []. Все умные указатели реализованы в виде шаблонов, что позволяет использовать их для любых встроенных и пользовательских типов.

Прежде чем переходить к рассмотрению реализаций умных указателей, следует обратить внимание на проблемы, связанные с использованием обычных указателей:
  1. Указатель не управляет временем жизни объекта, ответственность за удаление объекта целиком лежит на программисте. Проще говоря, указатель не «владеет» объектом.
  2. Указатели, ссылающиеся на один и тот же объект, никак не связаны между собой. Это создаёт проблему «битых» указателей – указателей, ссылающихся на освобождённые или перемещённые объекты.
  3. Нет никакой возможности проверить, указывает ли указатель на корректные данные, либо «в никуда».
  4. Указатель на единичный объект и указатель на массив объектов никак не отличаются друг от друга.

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

Первый недостаток состоит в том, что указатель не владеет объектом, на который ссылается. Из-за этого приходится очень внимательно относиться к освобождению объекта, иначе возможны утечки. Рассмотрим тривиальный пример:

Код:
void SomeMethod()
{
    SomeClass * temp = new SomeClass;
    temp -> DoSomething();
    delete temp;
}
Всё выглядит довольно надёжно: создаём объект, используем, затем освобождаем. Но это только до тех пор, пока мы не вспомним об исключениях. Метод DoSomething вполне может выбросить исключение, в результате чего SomeMethod завершится, так и не освободив temp. Для того чтобы исключить утечку в данном случае, приходится писать примерно такой код:

Код:
void SomeMethod()
{
    SomeClass * temp = 0;
    try
    {
        temp = new SomeClass;
        temp -> DoSomething();
    }
    catch ( ... )
    {
        delete temp;
        throw;
    }
    delete temp;
}
Аналогичные нагромождения приходится возводить и в функциях с множественными точками выхода (т.е. с несколькими конструкциями return). А если объектов создаётся несколько, то код вообще разрастается до невероятных размеров.

Другой классический пример:

Код:
class MyClass
{
public:
    MyClass() : a_( new SomeClass ), b_( new SomeOtherClass ) {}
    ~MyClass() throw()
    {
        delete a_;
        delete b_;
    }

private:
    SomeClass * a_;
    SomeOtherClass * b_;
};
В этом случае, если конструктор SomeOtherClass выбросит исключение, экземпляр SomeClass будет утерян навсегда. Дело в том, что по стандарту C++ [1], при раскрутке стека в результате возникновения исключения уничтожаются только полностью созданные объекты, а MyClass таким не является – ведь управление не доходит до тела конструктора. В результате будут вызваны только деструкторы членов класса, а в случае MyClass деструкторы эти ничего не делают, т.к. a_ и b_ являются обыкновенными указателями. Всё это приводит к утечке памяти.

Очевидное решение: вместо обычного указателя использовать объект, хранящий указатель и освобождающий объект в своём деструкторе (такая технология называется RAII – Resource Acquisition Is Initialization – «Захват ресурса есть инициализация», более подробно об RAII можно почитать в [2], [3] и [4]). Именно таким поведением обладает шаблон auto_ptr из стандартной библиотеки C++.


Благодарности

Большое спасибо Валерию Артюхину, благодаря чьей критике эта статья стала лучше.
Ответить с цитированием
Ads
Ответ

Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Trackbacks are Вкл.
Pingbacks are Вкл.
Refbacks are Выкл.


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Помогите люде умные и добрые) r01ex Любые вопросы от новичков 7 19.03.2012 10:55
Куплю "умные" свичи D-link Pashtet1131 Куплю 0 01.04.2011 20:49
Указатели.Динамическая память Solnze2 Pascal 0 11.06.2010 10:55
Указатели и многомерные массивы rusl Вопросы начинающих программистов 7 09.06.2009 10:49
Задачка на массивы и указатели rum777 Вопросы начинающих программистов 3 04.05.2009 18:33
Умные люди помогите пожалуйста с выбором компа Ferdus Подбор комплектующих 22 26.08.2008 18:14
Наследование, указатели, объекты D_K С/С++ 9 30.07.2008 20:05
Уважаемые умные ребята помогите срочно! Joy Операционная система Windows 2 18.07.2008 15:16
Указатели, ссылки как их делать Bar С/С++ 18 05.03.2008 02:15
Указатели, списки MFC Ikanu Visual C++ 1 26.06.2006 12:35
Ограничения на указатели в VC++ 6.0 GI Visual C++ 1 24.04.2004 14:43



Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
Нardforum.ru - компьютерный форум и программирование, форум программистов