John Last Ответов: 1

Способы улучшения кода C++ для полного устранения утечек памяти


Какие вещи я могу попытаться сделать с моим существующим кодом, чтобы полностью вернуть память операционной системе? Прямо сейчас в куче остается 8 байт памяти. Кроме того, я получаю эти ошибки, когда запускаю свой код через Valgrind:

#ifndef LINKEDLIST
    #define LINKEDLIST  
    /** LinkedList Class Interface
    */
  //begining update  
 class CLinkedlist {
    private:
    struct node {
        node() : mData(0), mNext(nullptr) {}
        int mData;
        node* mNext;
	};
    typedef node *nodePtr;
        
    nodePtr mHead;
    // As already suggested these should be local variables
    nodePtr mCurr;
    nodePtr mTemp;
	public:
    CLinkedlist() : mHead(nullptr), mCurr(nullptr), mTemp(nullptr) {}
    /// end of update
    ~CLinkedlist();
    
        void AddNode( int);
        void DeleteNode( int );
        void Printlist();
    
    };
    
    #endif

    /** \file Linkedlist.cpp
    */

   
    /** Linkedlist Deconstructor
    */
    CLinkedlist::~CLinkedlist(){
      mCurr = mHead;
      mTemp = mHead;
      while (mCurr)
      {
    	mTemp = mCurr;
        mCurr = mCurr->mNext;
        cout << mTemp->mData << " was destroyed." <<endl;
        delete mTemp;
     
      }
      mHead = nullptr;
    }
    
    void CLinkedlist::AddNode( int datanode){
      nodePtr data = new node;
      data->mData = datanode;
    
      if (mHead)
      {
        /// starting from the head, walking down the list
        mCurr = mHead;
        while(mCurr->mNext != nullptr) {
          mCurr = mCurr->mNext;
        }
        mCurr->mNext = data;
      }
    
      else
      {
        mHead = data;
      }
    
    }
    
    void CLinkedlist:: DeleteNode(int deldata){
      nodePtr delptr = nullptr;
      mTemp = mHead;
      mCurr = mHead;
      /// The program either walked the entire list and found
      /// nothing or we found our data
      while (mCurr != nullptr && mCurr->mData !=deldata ){
        mTemp =mCurr;
        mCurr = mCurr->mNext;
      }
      if (!mCurr){
        cout << deldata << " was not in the list\n";
        delete delptr;
      }
      else {
        /// new code
        if (mHead == mCurr)
           mHead = nullptr;
        /// old code
        delptr = mCurr;
        mCurr = mCurr->mNext;
        mTemp ->mNext=mCurr;
        delete delptr;
        cout << "The value "<< deldata << " was deleted\n";
      }
    }
    /** Displays information about our current list
    */
    void CLinkedlist::Printlist(){
      mCurr = mHead;
      while (mCurr)
      {
        cout << mCurr->mData << " -> " << endl;
        mCurr = mCurr->mNext;
      }
    }

    /** 
     * \file main.cpp
     */
    #include <cstdlib>
    #include "Linklist.h"
    
    using namespace std;
    
    int main (){
      CLinkedlist John;
    
      John.AddNode(3);
      //John.DeleteNode(3);
      //John.AddNode(5);
      //John.AddNode(7);
      //John.Printlist();
    
    }


Кроме того, я получаю различные ошибки на Valgrind, подобные приведенным ниже. На основе неинициализированных значений

=24530== Use of uninitialised value of size 4
==24530==    at 0x4AFB934: free_mem (in /lib/arm-linux-gnueabihf/libc-2.19.so)
==24530==    by 0x4AFB3C3: __libc_freeres (in /lib/arm-linux-gnueabihf/libc-2.19.so)
==24530==    by 0x4023633: _vgnU_freeres (vg_preloaded.c:61)
==24530==    by 0x48C5ED7: std::ctype<char>::_M_widen_init() const (in /usr/lib/arm-linux-gnueabihf/libstdc++.so.6.0.20)
==24530== 
==24530== Conditional jump or move depends on uninitialised value(s)
==24530==    at 0x4AFB9B0: free_mem (in /lib/arm-linux-gnueabihf/libc-2.19.so)
==24530==    by 0x4AFB3C3: __libc_freeres (in /lib/arm-linux-gnueabihf/libc-2.19.so)
==24530==    by 0x4023633: _vgnU_freeres (vg_preloaded.c:61)
==24530==    by 0x48C5ED7: std::ctype<char>::_M_widen_init() const (in /usr/lib/arm-linux-gnueabihf/libstdc++.so.6.0.20)
==24530== 
==24530== 
==24530== HEAP SUMMARY:
==24530==     in use at exit: 8 bytes in 1 blocks
==24530==   total heap usage: 1 allocs, 0 frees, 8 bytes allocated
==24530== 
==24530== LEAK SUMMARY:
==24530==    definitely lost: 0 bytes in 0 blocks
==24530==    indirectly lost: 0 bytes in 0 blocks
==24530==      possibly lost: 0 bytes in 0 blocks
==24530==    still reachable: 8 bytes in 1 blocks
==24530==         suppressed: 0 bytes in 0 blocks

Какие вещи я могу попытаться сделать с моим существующим кодом, чтобы полностью вернуть память операционной системе?
Для тестирования я использовал Blueberry Pi в операционной системе Linux. Кроме того, я использовал компилятор g++ и включил флаги-std=c++11, если это поможет.

Что я уже пробовал:

Я провел тщательное исследование по расшифровке утечек моей памяти, но либо то, что я нашел, было запрограммировано через c, либо не применимо в моей ситуации. Одна из проблем, которую я обнаружил, что потенциально я не инициализировал свои значения, и именно поэтому я получал эти ошибки. Однако после тщательной проверки я инициализирую все свои объявления в классе. Я смог проследить проблему до своего деструктора, по-видимому, он не освобождает память должным образом. Однако это может быть более глубокая проблема. Мой метод DeleteNode () при вызове в main способен освободить память, но я не знаю, как и почему он может это сделать, но не мой деструктор.

Обновление
Я пробовал различные методы, предложенные на этом форуме, но, к сожалению, это не сработало. Я подключился к различным Raspberry Pies, включая Blueberry, raspberry и pennutbutter для тестирования на моей виртуальной машине Linux, но всякий раз, когда я использую Valgrind для тестирования своего приложения, в дополнение к сообщению об ошибке, которое я уже показал, я также получаю незаконную инструкцию (не показано). Я также использовал Putty, но он имел тот же эффект. Однако я вроде как сдался, Visual Studio не сообщает, что у меня есть утечки, и вы, ребята, тоже, так что я, честно говоря, не знаю, и вы, ребята, похоже, не нашли ничего плохого в том, как я обрабатываю выделение памяти и освобождение, поэтому я не знаю.

Jochen Arndt

Я протестировал ваш код с Ubuntu, и он не сообщает об утечках памяти.

Тем не менее, похоже, что вы используете Raspberry Pi или другую встроенную систему на базе ARM. Вы можете добавить эту информацию и используемую командную строку G++ в свой вопрос, чтобы другие могли попытаться воспроизвести ошибку. Используйте зеленую ссылку "улучшить вопрос", чтобы отредактировать свой вопрос.

Mohibur Rashid

пара вопросов
если ваш mHead будет удален, вам также нужно установить mHead=nullptr, а также mCurr=nullptr
в противном случае он выдаст ошибку после удаления первого элемента. чтобы решить эту проблему, просто добавьте верификацию вверху, чтобы проверить, совпадает ли ваш поисковый элемент с mHead, и следуйте соответствующим инструкциям.

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

Почему ваш mTemp является глобальным?

John Last

Хорошо, да, mTemp-это глобальная переменная. Я понимаю вашу точку зрения, я добавил проверку, поэтому всякий раз, когда я удалял новый узел, я проверял, является ли он головным узлом, и устанавливал его в nullptr, прежде чем начать удаление (на самом деле я просто обновил код, чтобы отразить это). Кроме того, по умолчанию любой новый узел, который я добавляю, это mNext, будет nullptr, а его данные равны 0. Я попробовал этот же код в visual studio 2015, и он не сообщает об утечках памяти, но когда я попробовал на виртуальной машине linux через терминал, он говорит, что я все еще утечка памяти.

Mohibur Rashid

Я не должен был называть переменную-член глобальной переменной. В любом случае
valgrind не показывает утечки памяти в ubuntu

== 19837 = = Memcheck, детектор ошибок памяти
== 19837 = = Copyright (C)2002-2015, и GNU GPL'D, Джулиан Сьюард и др.
==19837== с помощью Valgrind и-3.12.0 и LibVEX, повтор-с -ч авторских прав информация
= = 19837= = команда:. / output
==19837==
Значение 3 было удалено
5 ->
7 ->
5 был уничтожен.
7 был уничтожен.
==19837==
= = 19837 = = СВОДКА КУЧИ:
= = 19837== используется при выходе: 0 байт в 0 блоках
== 19837 = = общее использование кучи: 5 выделений, 5 освобождений, 73 776 выделенных байт
==19837==
= = 19837== все блоки кучи были освобождены - утечки невозможны
==19837==
= = 19837== для подсчета обнаруженных и подавленных ошибок выполните повторный запуск с помощью:- v
== 19837 = = сводка ошибок: 0 ошибок из 0 контекстов (подавлено: 0 из 0)

проверено также с помощью опции-v. Так что я ни о чем не догадываюсь

1 Ответов

Рейтинг:
2

Jochen Arndt

Похоже, что это проблема, зависящая от компилятора / платформы / LIBC. Как уже отмечалось, я протестировал код (Ubuntu 14.04.05 LTS 32 bit, GCC 4.8.4) и не имел утечки памяти.

Сообщение "Использование неинициализированного значения размера 4" может указывать на наличие неинициализированного значения (указатель, переданный free_mem).

На всякий случай я предлагаю использовать конструкторы для инициализации переменных-членов. Я не уверен, что ваш метод гарантирует, что члены всегда инициализируются.

class CLinkedlist {
private:
    struct node {
        node() : mData(0), mNext(nullptr) {}
        int mData;
        node* mNext;
    };
    typedef node *nodePtr;
        
    nodePtr mHead;
    // As already suggested these should be local variables
    nodePtr mCurr;
    nodePtr mTemp;
public:
    CLinkedlist() : mHead(nullptr), mCurr(nullptr), mTemp(nullptr) {}
    // ...
};


John Last

Я попробовал твой метод, но, к сожалению, он не сработал. Я подключился к различным малиновым пирогам, включая Blueberry, raspberry и pennutbutter, чтобы протестировать их на своей виртуальной машине Linux, но всякий раз, когда я использую Valgrind для тестирования своего приложения, в дополнение к сообщению об ошибке, которое я уже показал, я также получаю незаконную инструкцию(не показано). Кроме того, я использовал замазку, но она имела тот же эффект. Тем не менее, я вроде как сдался, как я уже сказал в Visual Studio 2015, он не сообщает, что у меня есть какие-либо утечки, так что я честно не знаю, и вы, ребята, кажется, не находите ничего плохого в том, как я обрабатываю память, так что я не знаю.