Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

[C++] STL и память

10K
07 февраля 2012 года
Cybernetic
106 / / 22.07.2009
Есть некая сложная переходная структура, которую периодически приходится чистить, чтобы не загадить память:

Код:
class Node
{
public:
  int Value1;
  int Value2;

 std::map<int, int> Links1;
 std::map<int, int> Links2;
};

class Tree
{
private:
  std::vector<Node*> _nodes; //дерево, выложенное в линию
}

class Algorithm
{
private:
  Tree* _parameters; //указатель на динамический массив параметров
}


И вот в классе Algorithm мне нужно периодически удалять все объекты Tree и все, что они содержат.
(STL-вские контейнеры были выбраны в связи с частым изменением параметров. А то, что дерево выложено в струнку - это просто исторически сложилось.)

Во интернетах пишу, что контейнеры просто так не очистишь, и память они сами не освободят. Я даже писал:

 
Код:
class Tree
{
public:
  ~Tree()
  {
    for (std::vector<Node*>::iterator iter = _nodes.begin(); iter != _nodes.end(); iter++)
      delete *iter;
  }
}


Но что писать в деструктуре Node, я уже не представляю.

А память утекает оооочень стремительно... Как быть?
24K
07 февраля 2012 года
cn_venom
11 / / 23.01.2007
Вместо этого
 
Код:
for (std::vector<Node*>::iterator iter = _nodes.begin(); iter != _nodes.end(); iter++)
      delete *iter;

можно использовать умные указатели http://www.rsdn.ru/article/cpp/smartptr.xml.

Пример использования:
 
Код:
std::vector<std::tr1::shared_ptr<Node> > _nodes;

В результате все объекты Node при очистке вектора удалаться сами.

Цитата: Cybernetic
А память утекает оооочень стремительно... Как быть?


Я так понял, речь идёт о резервировании пямяти контейнером _nodes. Чтобы принудительно сбросить резерв, можно воспользоваться трюком:

 
Код:
_nodes.swap(std::vector<std::tr1::shared_ptr<Node> >());
10K
07 февраля 2012 года
Cybernetic
106 / / 22.07.2009
Цитата: cn_venom
Вместо этого можно использовать умные указатели http://www.rsdn.ru/article/cpp/smartptr.xml.


Какая интересная штука. Никогда раньше не встречал. Спасибо! Завтра попробую.
Но в статье не описана производительность таких указателей. На опыте личном не ощущали разницу во времени работы?

24K
07 февраля 2012 года
cn_venom
11 / / 23.01.2007
Согласно стандарта, умные указатели не должны заметно влиять на производительность. Дополнительное время тратится на копирование, создание и удаление УУ (обсуждение). Практически любые потери производительности, которые могут возникнуть при использовании УУ окупаются стабильностью программы и исключением ошибок, связанных с использованием "нативных" указателей. На личном опыте значимой разницы не возникало.
240
08 февраля 2012 года
aks
2.5K / / 14.07.2006
Цитата: cn_venom
Вместо этого
можно использовать умные указатели


Только не std::auto_ptr ни в коем случае в данном примере.

24K
08 февраля 2012 года
cn_venom
11 / / 23.01.2007
Цитата: aks
Только не std::auto_ptr ни в коем случае в данном примере.



Статья про УУ, приведённая выше раскрывает эту проблему:

Цитата:

Шаблон auto_ptr обеспечивает владение экземпляром объекта и его корректную очистку при выходе из области видимости – иными словами, реализует принцип RAII. Тем не менее, следует принимать во внимание необычную семантику копирования и присваивания – разрушающее копирование. В частности, эта семантика не позволяет использовать auto_ptr в контейнерах стандартной библиотеки.
Выходом из этой ситуации является реализация умного указателя с подсчётом ссылок – shared_ptr.

240
08 февраля 2012 года
aks
2.5K / / 14.07.2006
Цитата: cn_venom
Статья про УУ, приведённая выше раскрывает эту проблему:


Да но очень вскользь. )

10K
08 февраля 2012 года
Cybernetic
106 / / 22.07.2009
Пока умные указатели я оставил на потом...

Все таки STL на первый взгляд и ни причем. Пытаюсь использовать crtdbg для утечек... Утечек показывает много, а вот найти их пока не удается. Причем проблема кроется гораздо глубже, чем в описанном выше коде. Эхх..

А утечка памяти может возникать без явного использования оператора new(malloc)?
240
08 февраля 2012 года
aks
2.5K / / 14.07.2006
Цитата: Cybernetic

А утечка памяти может возникать без явного использования оператора new(malloc)?


Это как учитывая, что у тебя там указатели, которым ты делаешь delete? =)

10K
08 февраля 2012 года
Cybernetic
106 / / 22.07.2009
Цитата: aks
Это как учитывая, что у тебя там указатели, которым ты делаешь delete? =)


Немножко не понял вопроса.... Там - это в коде, указанном в топике?) Про него можно забыть, проблема глубже. Я же не писал функций, которые обрабатывают и вычисляют всю ту байду. Вот там и есть утечки. Например, есть метод который... А, к черту слова. Код:

Здесь голова всего, нужно получить вероятность из структуры в зависимости от параметра newHistory, а также выписать некоторый кусок входного параметра в maxSubHistory;

 
Код:
FullHistory* newHistory = currentState->AppendLetter(iter->first);
FullHistory* maxSubHistory;

_CrtMemState ms;
_CrtMemCheckpoint(&ms);

Nullable<double> modelProbability = _modelContainer->GetProbability(newHistory, &maxSubHistory);

_CrtMemDumpAllObjectsSince(&ms);


Обертка структуры:
 
Код:
Nullable<double> ModelVLMM::GetProbability(const FullHistory* inputHistory, FullHistory** maxSubHistory)
{
    std::wstring maxLetterSubHistory;

    Nullable<double> probability =
        _ngramContainer->GetProbability(inputHistory->GetConstString(), maxLetterSubHistory);

    *maxSubHistory = new FullHistory(maxLetterSubHistory);
    return probability;
}


Та самая жесткая функция, ради которой всё:
Код:
double NGramContainer::GetProbability(const std::wstring &inputString, std::wstring & outputString)
{
    std::list<wchar_t> maxSubHistory;

    int length = inputString.size();

    unsigned int oldShift = 0;
    unsigned int newShift;
    for ( int i = length - 1; i >= 0; i-- )
    {
        unsigned int currentLetterCode = (unsigned int) _alphabet->EncodeLetter(inputString);
        newShift = _links[oldShift + currentLetterCode];

        if ( newShift == 0 )
            break;

        maxSubHistory.push_front(inputString);
        oldShift = newShift;
    }

    if (maxSubHistory.size() == 0)
        outputString = inputString[length - 1];
    else
        outputString = std::wstring(maxSubHistory.begin(), maxSubHistory.end());

    oldShift = 0;
    newShift = 0;
    unsigned int currentLetter;
    for ( int i = length - 2; i >= 0; i-- )
    {
        currentLetter = (unsigned int) _alphabet->EncodeLetter(inputString);
        newShift = _links[oldShift + currentLetter];

        if ( newShift == 0 )
        {
            currentLetter = (unsigned int) _alphabet->EncodeLetter(inputString[length - 1]);
            return _values[oldShift + currentLetter];
        }

        oldShift = newShift;
    }

    currentLetter = (unsigned int) _alphabet->EncodeLetter(inputString[length - 1]);
    return _values[newShift + currentLetter];
}


И пояснения, что собой представляют структуры:
Код:
class FullHistory
{
public:
    std::vector<WordIdDictionary> WordHistory;
    std::wstring LetterHistory;
}


class WordIdDictionary
{
public:
    unsigned int WordId;
    std::wstring* Dictionary;
}


template<class T> class Nullable
{
private:
    T _value;
    bool _hasValue;
}


//К слову, от STL я инкуда не ушел, поэтому топик даже остается пока актульным :D

Так вот, за весь стек вызовов я делаю всего ОДИН new. Но crtdbg-отчет утверждает, что у меня аж 6 блоков памяти вытекло в никуда.
Код:
Dumping objects ->
{61891} normal block at 0x074AD208, 8 bytes long.
 Data: < 3I     > 20 33 49 07 00 00 00 00
{61890} normal block at 0x074AD1D0, 8 bytes long.
 Data: <43I     > 34 33 49 07 00 00 00 00
{61887} normal block at 0x07493320, 52 bytes long.
 Data: <  J             > 08 D2 4A 07 00 00 00 00 00 00 00 00 00 00 00 00
{61881} normal block at 0x074AD010, 8 bytes long.
 Data: < 3I     > 94 33 49 07 00 00 00 00
{61880} normal block at 0x074ACFD8, 8 bytes long.
 Data: < 3I     > 80 33 49 07 00 00 00 00
{61879} normal block at 0x07493380, 52 bytes long.
 Data: <  J             > D8 CF 4A 07 00 00 00 00 00 00 00 00 00 00 00 00
Object dump complete.

Откуда они вообще взялись?

[COLOR="red"]UPD: Правда, в логе не было фразы "Detected memory leaks!". Нужно было другую функцию использовать для отловли утечек(_CrtDumpMemoryLeaks())?[/COLOR]
24K
09 февраля 2012 года
cn_venom
11 / / 23.01.2007
Покажите _alphabet->EncodeLetter. Еще можно переопределить оператор new, где построить собственный отчёт о выделяемых блоках, и, что более важно - установить точнку останова в отладчике на выделение памяти.
10K
09 февраля 2012 года
Cybernetic
106 / / 22.07.2009
Цитата: cn_venom
Покажите _alphabet->EncodeLetter. Еще можно переопределить оператор new, где построить собственный отчёт о выделяемых блоках, и, что более важно - установить точнку останова в отладчике на выделение памяти.



_alphabet->EncodeLetter возвращает из std::map<wchar_t, unsigned char> некий (собственноручно созданный)код символа и выглядит так:

 
Код:
int EncodeLetter(wchar_t letter)
{
  std::map<...>::iterator = _codedDictionary.find(letter);
  if (iterator == _codedDictionary.end())
    throw "error";
  return iterator->second;
}


Но вообще можете не разбираться. Ответ на свои вопросы я так и не нашел, а просто пошел другим способом.
Я нашел места, где зачем то делал "new". Делал я это для того, чтобы в функции передавать без копирования в памяти. Почему я сразу не использовал создание элемента в стеке, а не в куче, и почему я не пользовался передачей параметров по ссылке, я не знаю. Но когда я перестал писать new и стал пользоваться ссылками, то утечки прекратились. И STL тут оказался совсем ни причем.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог