簡體   English   中英

Valgrind使用shared_ptr c ++ 11指示內存泄漏

[英]Valgrind indicates memory leak with shared_ptr c++11

我已經使用C ++ 11智能指針實現了鏈接列表。 此實現使用shared_ptr存儲內部數據結構,該結構用於實現其隱式共享。 我在下面提供了源代碼的相關部分:

namespace Algos {

template <typename T>
struct LinkedListData{
    struct Node {
        std::unique_ptr<Node> next;
        Node *prev = nullptr;
        T data;
    };

    std::unique_ptr<Node> root;
    Node *last = nullptr;
    int size = 0;

    LinkedListData() {
        root = std::make_unique<Node>();
        root->prev = nullptr; //last virtual element
        root->next = std::make_unique<Node>();
        last = root->next.get();
        last->prev = root.get();
        last->next = nullptr;
    }

    //deferr pointers manually to avoid stackoverflow due to 
    //recursion explosion
    static void cleanup(LinkedListData<T> *data) { 
        #ifdef DEBUG_TXT 
            int nodeCount=0;
        #endif
        Node *n = data->last;
        if(n==nullptr) { return; }
        while(n) {
            #ifdef DEBUG_TXT 
                if(n->next.get())
                    std::cout << "Release {n->next()} [" << ++nodeCount  << 
                    "]: "<< n->next.get() << std::endl;
            #endif
            n->next.release();
            ALGO_ASSERT(n->next.get() == nullptr, "Node reference not deferred");
            n = n->prev;
        }
        data->size = 0;
        #ifdef DEBUG_TXT
            std::cout << "Release {Root} [" << ++nodeCount  << "]: "<< data->root.get() << std::endl;
        #endif
        data->root.release();
    } 
};

template <class T>
class LinkedList {

    typedef typename LinkedListData<T>::Node node_type;
    std::shared_ptr<LinkedListData<T> > d;

    public:

        LinkedList() : d(new LinkedListData<T>(), LinkedListData<T>::cleanup){}

/* Code omitted .... */

};

}

由於使用new ,以下代碼在shared_pointer構造函數中的valgrind上觸發內存泄漏錯誤:

#include <iostream>
#include <string>
#include <unistd.h>

// #define DEBUG_TXT

#include "global/assert.h"

#include "linked_list/linkedlist.h"

#define TEST_SIZE 5000

struct DataTest {
   int integer;
   bool boolean;
   std::string txt;
   DataTest& operator=(const DataTest& other) {
      integer = other.integer;
      boolean = other.boolean;
      txt = other.txt;
      return (*this);
   }

   bool operator==(const DataTest& other) const {
      return (
         integer == other.integer &&
         boolean == other.boolean &&
         txt == other.txt
      );
    }   
};

struct Data {
    DataTest data[TEST_SIZE];
    const int n = TEST_SIZE;

    static void initDataSample(Data &d) {
        for(int i=0; i<d.n; i++) {
            d.data[i].integer = i;
            d.data[i].boolean = (i%2 == 0);
            d.data[i].txt = "abc";
        }
    }
};

void appendElements(Algos::LinkedList<DataTest> &l, const Data& d){
    for(int i=0; i<d.n; i++) {
        l.append(d.data[i]);
    }
}

void prependElements(Algos::LinkedList<DataTest> &l, const Data& d) {
    for(int i=d.n-1; i>=0; i--) {
        l.prepend(d.data[i]);
    }
}



int main(int argv, char* argc[]) {

   Data d;  
    Data::initDataSample(d);

    Algos::LinkedList<DataTest> l1;
    {
        Algos::LinkedList<DataTest> l2;
        l1 = l2;
    }

    sleep(2);

    appendElements(l1, d);

    int removeSize = l1.size()/2;

    for(int i=0; i<removeSize; i++)
        l1.takeFirst();


    prependElements(l1, d);

    removeSize = l1.size()/2;
    for(int i=0; i<removeSize; i++)
        l1.takeLast();


   return 0;
}

這是我在valgrind控制台中收到的消息:

> ==8897== HEAP SUMMARY:
==8897==     in use at exit: 282,976 bytes in 3,757 blocks
==8897==   total heap usage: 10,009 allocs, 6,252 frees, 633,040 bytes allocated
==8897== 
==8897== 136 (24 direct, 112 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 8
==8897==    at 0x4C2E216: operator new(unsigned long) (vg_replace_malloc.c:334)
==8897==    by 0x407C1C: Algos::LinkedList<DataTest>::LinkedList() (linkedlist.h:74)
==8897==    by 0x4074B4: main (insert_delete_rounds.cpp:63)
==8897== 
==8897== 210,136 (24 direct, 210,112 indirect) bytes in 1 blocks are definitely lost in loss record 8 of 8
==8897==    at 0x4C2E216: operator new(unsigned long) (vg_replace_malloc.c:334)
==8897==    by 0x407C1C: Algos::LinkedList<DataTest>::LinkedList() (linkedlist.h:74)
==8897==    by 0x4074C3: main (insert_delete_rounds.cpp:65)
==8897== 
==8897== LEAK SUMMARY:
==8897==    definitely lost: 48 bytes in 2 blocks
==8897==    indirectly lost: 210,224 bytes in 3,754 blocks
==8897==      possibly lost: 0 bytes in 0 blocks
==8897==    still reachable: 72,704 bytes in 1 blocks
==8897==         suppressed: 0 bytes in 0 blocks
==8897== Reachable blocks (those to which a pointer was found) are not shown.
==8897== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==8897== 
==8897== For counts of detected and suppressed errors, rerun with: -v
==8897== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

據我所知,沒有其他方法可以在不使用C ++ 11中使用new的情況下使用自定義Deleter來啟動shared_ptr。 在閱讀了文檔和關於stackoverflow的不同線程之后,我注意到std :: make_shared()不支持傳遞自定義Deleter 因此,我的問題是:此內存泄漏警告合法嗎? 如果是,可以避免嗎?

開發工具設置:

  • gcc:5.4.0
  • 瓦爾格朗德:3.13.0
  • 操作系統:Linux Ubuntu 16.04

要回答您的問題:

  1. 您可以使用allocate_shared來允許使用自定義內存分配器。 簽名是相同的,除了它將對分配器的const引用作為第一個參數。

  2. 盡管您可以清理LinkedListData的內部結構,但是從不delete cleanup方法中分配的指針。 應該從LinkedListData析構函數調用cleanup ,並且應該使用常規刪除程序(如果使用自定義分配器,則應使用自定義刪除程序)在LinkedListData指針上調用delete。

簡而言之,此行將始終導致內存泄漏:

LinkedList() : d(new LinkedListData<T>(), LinkedListData<T>::cleanup){}

您創建了一個new LinkedListData<T>() ,但是您沒有在cleanup對其調用delete

由於您在內部使用智能指針,因此不應將LinkedListData::cleanup指定為自定義刪除器,而應使用默認值。 如果您嘗試使用智能指針,那么如果您沒有顯式調用delete ,最好避免使用new調用。 因此,可以僅使用d(std::make_shared<LinkedListData<T>>())來初始化d成員。 同樣,可以將其聲明為LinkedListData<T> d; 那里沒有任何智能指針。

然后,您不應在LinkedListData::cleanup調用release 此方法從智能指針包裝分離原始指針,並且不取消分配關聯的對象。 您可能需要reset 但是再一次,該方法完全沒有必要,因為您的所有指針都是智能的,並且在調用父析構函數時應自動清除關聯的數據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM