简体   繁体   中英

C++ Free memory

I would like to ask how to properly free memory in my program. (free(): double free detected in tcache 2 timeout: the monitored command dumped core)

In the adVersion method, I create a linked list with the current instance values. In the variable m_History I have a reference to the following element. In the method, I add the current contents of the instance to the end of this linked list.

In the copy constructor, I copy everything and the linked list belonging to the given instances.

I enclose important parts of the program and in the link the whole program https://onecompiler.com/cpp/3xzaa3mfh

//EDIT: I little change the adVersion method, please can you check if it is OK?

    class CFile
    {
            uint8_t *m_Data;
            uint32_t m_Len;
            uint32_t m_Position;
            uint32_t m_Capacity;
    
            size_t m_LenHistory;
            CFile *m_History;
    
    };
    
    CFile::CFile(void)
    :m_Data(nullptr), m_Len(0), m_Position(0), m_Capacity(0),m_LenHistory(0), m_History(nullptr)
    {
    }
    
    CFile::~CFile (void)
    {
        delete[] m_Data;
    
        //Delete linked list
        while (m_History)
        {
            CFile* old = m_History;
            delete[] old->m_Data;
            m_History = m_History->m_History;
            delete old;
        }
        m_History = nullptr;
    }
    
    CFile* copyLinkedList(CFile *list)
    {
        if(list == nullptr) return nullptr;
    
        CFile* result = new CFile;
        result->m_Capacity = list->m_Capacity;
        result->m_Len = list->m_Len;
        result->m_Position = list->m_Position;
        result->m_LenHistory = list->m_LenHistory;
        result->m_Data = new uint8_t[list->m_Capacity];
        memcpy(result->m_Data, list->m_Data, result->m_Len);
        result->m_History = copyLinkedList(list->m_History);
        
        return result;
    }
    
    // Copy construkctor
    CFile::CFile(const CFile &src)
    : m_Data(new uint8_t[src.m_Capacity]),
      m_Len(src.m_Len),
      m_Position(src.m_Position),
      m_Capacity(src.m_Capacity),
      m_LenHistory(src.m_LenHistory)
    {
        memcpy(m_Data, src.m_Data, m_Len);
        m_History = copyLinkedList(src.m_History);
    }
    
    void CFile::copyWithoudLinkedList( CFile &src )
    {
        m_Capacity = src.m_Capacity;
        m_Len = src.m_Len;
        m_LenHistory = src.m_LenHistory;
        m_Position = src.m_Position;
        m_Data = new uint8_t[src.m_Capacity];
    
        memcpy(m_Data, src.m_Data, m_Len);
    }
    
    void CFile::addVersion  ( void )
{
    CFile *tmp = m_History;
    CFile *prev;

    if(!tmp)
    {
        tmp = new CFile();
        tmp->copyWithoudLinkedList(*this);
        this->m_History = tmp;
        this->m_History->m_History = nullptr;
        m_LenHistory++;
    }
    else
    {
        while(tmp)
        {
            prev = tmp;
            tmp = tmp->m_History;
        }
        
        tmp = new CFile();
        tmp->copyWithoudLinkedList(*this);
        prev->m_History = tmp;
        prev->m_History->m_History = nullptr;
        m_LenHistory++;
   }
}

The best way to avoid problems with your manual memory handling is not having any. Large parts of the C++ standard library are there so that you don't have to bother with implementing standard data structures.

First off, instead of using uint8_t *m_Data; (which requires allocating and freeing memory) use std::vector< uint8_t > which does it for you.

Second, turn class CFile from doing two things -- holding data, and implementing a list -- into doing just one, holding data.

#include <vector>

struct CFile
{
    std::vector< uint8_t > m_Data;
    // ...
};

Third, get a linked list of CFile instances via the standard container std::forward_list :

#include <forward_list>

std::forward_list< CFile > cfile_list;
CFile cfile { { 0x23, 0x42, 0xff } };
cfile_list.push_front( cfile );

Since you now don't have to assign memory manually at any place, you also don't have to release it manually either.

While I totally agree with the other people that you shouldn't be managing memory yourself, unless you are developing a new data structure not available in the standard library, I also like people who want to "do it themselves" to learn and make mistakes.

So, since I'm on vacation, I took the chance to redo it my way (not with much attention, so don't trust this too much).

The basic idea is to separate the content of file from the manager of the versioning system. I've tried to avoid changing names and algorithms from yours too much, but the lists stuff was really too strange and filled of potentially uninitialized variables.

I didn't add anything related to move constructors, but they are definitely important if you plan to manage memory yourself. Take a look at the copy-and-swap idiom.

See if this fits your needs:

#include <cassert>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <iostream>
#include <algorithm>

// using namespace std; // Not a good idea

class CFile {
    struct CFileContents {
        uint8_t *m_Data = nullptr;
        uint32_t m_Len = 0;
        uint32_t m_Capacity = 0;
        uint32_t m_Position = 0;
    
        CFileContents() {}
        CFileContents(const CFileContents& other) :
            m_Data(new uint8_t[other.m_Capacity]),
            m_Len(other.m_Len),
            m_Capacity(other.m_Capacity),
            m_Position(other.m_Position)
        {
            std::copy(other.m_Data, other.m_Data + other.m_Len, m_Data);
        }
        CFileContents& operator=(const CFileContents& rhs) {
            if (m_Capacity < rhs.m_Capacity) {
                delete[] m_Data;
                m_Capacity = rhs.m_Capacity;
                m_Data = new uint8_t[m_Capacity];
            }
            m_Len = rhs.m_Len;
            std::copy(rhs.m_Data, rhs.m_Data + rhs.m_Len, m_Data);
            m_Position = rhs.m_Position;
            return *this;
        }
        ~CFileContents() {
            delete[] m_Data;
        }

        const uint8_t* data() const { return m_Data; }
        uint32_t length() const { return m_Len; }
        uint32_t pos() const { return m_Position; }

        void truncate(void) {
            m_Len = m_Position;
        }

        uint32_t write(const uint8_t *src, uint32_t bytes) {
            if (m_Position + bytes > m_Capacity)
            {
                while (m_Position + bytes > m_Capacity)
                    m_Capacity += m_Capacity / 2 + 10;

                uint8_t *tmp = new uint8_t[m_Capacity];
                std::copy(m_Data, m_Data + m_Len, tmp);
                delete[] m_Data;
                m_Data = tmp;
            }
            std::copy(src, src + bytes, m_Data + m_Position);

            if (m_Position + bytes > m_Len)
                m_Len = m_Position + bytes;

            m_Position += bytes;
            return bytes;
        }

        uint32_t read(uint8_t* dst, uint32_t bytes)
        {
            if (m_Position + bytes > m_Len) {
                bytes = m_Len - m_Position;
            }
            std::copy(m_Data + m_Position, m_Data + m_Position + bytes, dst);
            m_Position += bytes;
            return bytes;
        }

        bool seek(uint32_t offset) {
            if (offset <= m_Len) {
                m_Position = offset;
                return true;
            }
            return false;
        }

    };

    struct CNode {
        CFileContents data;
        CNode* next = nullptr;

        CNode() {}
        CNode(const CNode& other) = delete;
        CNode& operator=(const CNode& rhs) = delete;
        ~CNode() {
            delete next;
        }
    };

    uint32_t m_LenHistory;
    CNode *m_History;

public:
    CFile(void) : m_LenHistory(1), m_History(new CNode) {}

    friend void swap(CFile& a, CFile& b) {
        using std::swap;
        swap(a.m_LenHistory, b.m_LenHistory);
        swap(a.m_History, b.m_History);
    }

    CFile(const CFile &src) : m_LenHistory(src.m_LenHistory) {
        CNode** dcur = &m_History;
        for (CNode* scur = src.m_History; scur; scur = scur->next) {
            *dcur = new CNode;
            (*dcur)->data = scur->data;
            dcur = &(*dcur)->next;
        }
    }

    CFile& operator=(CFile src) {
        swap(*this, src);
        return *this;
    }

    ~CFile(void) {
        delete m_History;
    }

    uint32_t write(const uint8_t *src, uint32_t bytes) {
        return m_History->data.write(src, bytes);
    }
    uint32_t read(uint8_t* dst, uint32_t bytes) {
        return m_History->data.read(dst, bytes);
    }
    bool seek(uint32_t offset) {
        return m_History->data.seek(offset);
    }
    void truncate(void) {
        m_History->data.truncate();
    }
    uint32_t fileSize(void) const { return m_History->data.length(); }

    void addVersion(void) {   
        CNode* head = new CNode;
        head->data = m_History->data;
        head->next = m_History;
        m_History = head;
        ++m_LenHistory;
    }

    bool undoVersion(void) {
        if (m_LenHistory == 1)
            return false;
        CNode* head = m_History;
        m_History = head->next;
        head->next = nullptr;
        delete head;
        --m_LenHistory;
        return true;
    }

    uint32_t lenHistory() const {
        return m_LenHistory;
    }
};



bool writeTest(CFile& x, const std::initializer_list<uint8_t>& data, uint32_t wrLen)
{
    return x.write(data.begin(), data.size()) == wrLen;
}

bool readTest(CFile& x, const std::initializer_list<uint8_t>& data)
{
    auto* tmp = new uint8_t[data.size()];
    uint32_t idx = 0;
    bool ret = true;

    if (x.read(tmp, data.size()) != data.size())
        ret = false;
    else {
        for (auto v : data) {
            if (tmp[idx++] != v) {
                ret = false;
                break;
            }
        }
    }
    delete[] tmp;
    return ret;
}



int main(void)
{
    {
        CFile f0;
        assert(writeTest(f0, { 10, 20, 30 }, 3));
        assert(f0.fileSize() == 3);
        assert(writeTest(f0, { 60, 70, 80 }, 3));
        assert(f0.fileSize() == 6);
        assert(f0.seek(2));
        assert(writeTest(f0, { 5, 4 }, 2));
        assert(f0.fileSize() == 6);
        assert(f0.seek(1));
        assert(readTest(f0, { 20, 5, 4, 70, 80 }));
        assert(f0.seek(3));
        f0.addVersion();
        assert(f0.seek(6));
        assert(writeTest(f0, { 100, 101, 102, 103 }, 4));
        f0.addVersion();
        assert(f0.seek(5));
        CFile f1(f0);
        f0.truncate();
        assert(f0.seek(0));
        assert(readTest(f0, { 10, 20, 5, 4, 70 }));
        assert(f0.undoVersion());
        assert(f0.seek(0));
        assert(readTest(f0, { 10, 20, 5, 4, 70, 80, 100, 101, 102, 103 }));
        assert(f0.undoVersion());
        assert(f0.seek(0));
        assert(readTest(f0, { 10, 20, 5, 4, 70, 80 }));
        assert(!f0.seek(100));
        assert(writeTest(f1, { 200, 210, 220 }, 3));
        assert(f1.seek(0));
        assert(readTest(f1, { 10, 20, 5, 4, 70, 200, 210, 220, 102, 103 }));
        std::cout << f1.lenHistory() << std::endl;
        //std::cout << f1.m_History->m_Data << std::endl;
        assert(f1.undoVersion());
        std::cout << f1.lenHistory() << std::endl;
        assert(f1.undoVersion());
        assert(readTest(f1, { 4, 70, 80 }));
        assert(!f1.undoVersion());
    }
    _CrtDumpMemoryLeaks(); // remove this if you are not using Visual Studio
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM