2013-08-14 3 views
-2

меня попросили оптимизировать гр проект ++ и там я уже сталкивался с этой «утечкой памяти» ситуация внутри кода класса (пример упрощен, но главная проблема ясна):C++ исправить утечку памяти

std::list<T*> _list; 
void func(){ 
    T* obj = some_func(); 
    if (!obj){ 
     obj = new T(); 
     _list.push_back(obj); // Here is a leak,we do not know when *obj will be removed from _list to call its destructor 
    } 
    obj->some_field = some_value; 
} 
/*_list will be used and managed somewhere else 
    and we do not know actually when, where and how.*/ 

Так как это можно исправить элегантно? К элегантным я имею в виду, не определяя свой собственный контейнер. Нужно ли использовать некоторые умные указатели?

ОБНОВЛЕНИЕ. Это не C++ 11. Так что нет полезных полезных материалов

+3

Как вам удалось нажать 'T *' в список 'T'? Имеет ли 'T' конструктор, который принимает' T * '? – juanchopanza

+1

Вы действительно наблюдали утечку памяти? Этот код является разумным, если список принадлежит владельцу объекта. –

+0

Я не думаю, что основная проблема понятна вообще. Не могли бы вы уточнить? – molbdnilo

ответ

3

Так как это можно исправить элегантно?

Храните объекты, если это возможно, или умные указатели иначе. Управление памятью через немые указатели - это рецепт утечек памяти и хуже.

std::unique_ptr был бы лучшим умным указателем - он соответствует семантике одного владельца (списка), с возможностью передачи прав собственности и выхода. Тем не менее, вы говорите, что вы застряли в прошлом, поэтому лучшим вариантом является, вероятно, shared_ptr от TR1 или Boost.

Нужно ли использовать некоторые интеллектуальные указатели?

Это, безусловно, самое простое решение, если вы не можете хранить объекты. Альтернативой является тщательный контроль за удалением указателей из списка, что позволяет либо удалить их, либо передать право собственности в четко определенном порядке (и что новый владелец выполняет свои обязанности правильно). Вы, по сути, определяете свой собственный контейнерный адаптер; хотя вы можете использовать готовое решение, такое как контейнеры указателей Boost.

0

Каков протокол для использования some_func? Предполагается ли, что вызывающий абонент возьмет на себя ответственность за возвращенный объект? Если это так, это будет утечка. Вам нужно нажать obj, возвращаемый some_func, в список, предполагая, что список переходит в собственность объекта. Действительно, использование unique_ptrs здесь поможет сделать это более понятным.

+0

hm, но что, если * obj, возвращаемый some_func, равен null? Мы не можем вытолкнуть его в _list. Поэтому основная проблема возникает, когда мы создаем новый экземпляр T с «новым» и нажимаем его внутри какого-либо глобального _list, и мы не знаем, когда он будет удален из _list (по-моему это на самом деле вызывает утечку памяти) – Seter

0

Предполагая, что вы храните указателей в список (std::list<T*> _list;), возможно, это может помочь:

namespace RAII 
{ 
    template<typename T> 
    class AUTO_LIST_WITH_POINTER 
    { 
    public: 
     AUTO_LIST_WITH_POINTER(){} 
     ~AUTO_LIST_WITH_POINTER() 
     { 
      // if you compiler does not support lambdas, replace this with 
      // a simple 'for' loop 
      std::for_each(list_.begin(), list_.end(), [](T * & listItem)throw() 
      { 
       delete listItem; 
      }); 
     } 

     const std::list<T*> & get()const 
     { 
      return list_; 
     } 

     std::list<T*> & get() 
     { 
      return list_; 
     } 

     std::list<T*> detach() // Warning: probably an expensive operation!!!! 
     { 
      std::list<T*> list(list_); 
      list_.clear(); 
      return list; 
     } 

    private: 
     std::list<T*> list_; 

     AUTO_LIST_WITH_POINTER(const AUTO_LIST_WITH_POINTER &);    // TODO: don't implement, or..?? the OP should know better 
     AUTO_LIST_WITH_POINTER & operator=(const AUTO_LIST_WITH_POINTER &); // TODO: don't implement, or..?? the OP should know better 
    }; 
} 
0

Один из вариантов - использовать RAII, полагаться на std::unique_ptr вместе с семантикой перемещения. Например:

std::list<std::unique_ptr<T>> list; 
auto obj = some_func(); 
int some_value = 1; 
if (!obj){ 
    std::unique_ptr<T> new_obj(new T()); 
    list.emplace_back(std::move(new_obj)); 
    obj = list.back().get(); 
} 
obj->some_field = some_value; 
+0

Это не C++ 11 Так что никаких полезных полезных вещей – Seter

+1

@Seter: Тогда 'boost :: shared_ptr' выполнит ту же работу, хотя и немного более накладные расходы. –

0

Без C++ 11 вы можете использовать std::auto_ptr<>. Это переносит право собственности на указатель на одного владельца, поэтому, если _list имеет тип std::list<std::auto_ptr<T>>, объекты будут удалены при уничтожении контейнера.

+0

Хранение 'auto_ptr' в контейнере - очень плохая идея. Он не соответствует требованиям CopyConstructible или Assignment для использования в стандартных контейнерах, и легко случайно скопировать один из контейнера и удалить объект. –

Смежные вопросы