如何解决 free(): double free detectors in tcache 2 make: *** [Makefile:88: test] Aborted (core dumped)

问题描述 投票:0回答:1

我正在做这个项目,我需要实现双链表类功能。但是,当我尝试在终端上运行 allocate() 函数测试时,我得到了 free(): 在 tcache 2make 中检测到双重释放:*** [Makefile:88: test] 中止(核心转储)

这是我迄今为止在项目中所做的事情:

#ifndef MY_DOUBLY_LINKED_LIST_HPP
#define MY_DOUBLY_LINKED_LIST_HPP


/**
 * TODO: Implement DoublyLinkedList, its Node, and its Iterator!
 * 
 * I've left some methods filled out for you,
 *  and stubbed out some structure, to reduce difficulty.
 * 
 * You may add or remove methods as you see fit,
 *  as long as you can still pass all unit tests.
 * Although, it may be more difficult to do so. Your choice!
 * 
 * Notice we're inside a namespace here.
 * The DLL is inside a namespace called DoublyLinkedList,
 *  which is itself inside a namespace called CPSC131
 * This means, if you'd like to play around with your class later,
 *  you'll need to access it like so:
 * ::CPSC131::DoublyLinkedList::DoublyLinkedList<int> list;
 * 
 * Look into main.cpp and CPP_Tests.cpp for examples of using
 *  the DLL and your BookStore. But don't worry too much, as you
 *  only need to implement these classes
 * (main and tests are already done for you)
 */


//
#include <iostream>
#include <stdlib.h>
#include <stdexcept>


/**
 * Namespace for our classroom !
 */
namespace CPSC131
{
    /**
     * Namespace to hold all things related to our DLL
     */
    namespace DoublyLinkedList
    {
        /**
         * Implement our DoublyLinkedList class !
         */
        template <class T>
        class DoublyLinkedList
        {
            public:
                
                /**
                 * Node class, representing a single item in our linked list
                 */
                // TODO: Complete all class methods
                class Node
                {
                    public:
                        
                        /// CTORS
                        Node() : prev_(nullptr), next_(nullptr) {}
                        Node(T element) {}
                        Node(T element, Node* prev, Node* next) {}
                        
                        /// Set the pointer to the previous element
                        void setPrevious(Node* prev) { prev_ = prev;}
                        
                        /// Set the pointer to the previous element
                        void setPrev(Node* prev) { prev_ = prev; }
                        
                        /// Get a pointer to the previous element
                        Node* getPrevious() { return prev_; }
                        
                        /// Get a pointer to the previous element
                        Node* getPrev() { return prev_; }
                        
                        /// Set the pointer to the next node
                        void setNext(Node* next) { next_ = next; }
                        
                        /// Get a pointer to the next node
                        Node* getNext() { return next_; }
                        
                        /// Set the element this node holds
                        void setElement(T element) { this-> element_ = element; }
                        
                        /// Get the element this node holds
                        /// YOUR WELCOME
                        T& getElement() { return this->element_; }
                        
                        /// Return a reference to the element
                        /// YOUR WELCOME
                        T& operator*() { return this->element_; }
                        
                    private:
                        T element_;
                        Node* prev_;
                        Node* next_;
                };
                
                /**
                 * Nested Iterator class.
                 * This allows user code to refer to the Iterator's type as:
                 * 
                 * CPSC131::DoublyLinkedList::DoublyLinkedList<int>::Iterator
                 * 
                 * (as opposed to specifying the template argument two times)
                 */
                class Iterator
                {
                    public:
                        
                        ///P's code
                        Iterator(){}
                        
                        ///P's code
                        Iterator(Node* head, Node* tail) : head_(head), tail_(tail)
                        {
                            this->cursor_ = this->end();
                        }
                        
                        /// Constructor taking a head, tail, and cursor pointer; YOUR WELCOME
                        Iterator(Node* head, Node* tail, Node* cursor) : head_(head), tail_(tail), cursor_(cursor) {}
                        
                        /// Get a pointer to the head node, or end() if this list is empty
                        Node* begin()
                        {
                            //  TODO: Your code here
                            if(head_ != nullptr) {
                                return head_;
                            }
                            return nullptr;
                        }
                        
                        /// Get a node pointer representing "end" (aka "depleted"). Probably want to just use nullptr.
                        Node* end() { return nullptr; }
                        
                        /// Get the node to which this iterator is currently pointing
                        Node* getCursor() { return cursor_; }
                        
                        /**
                         * Assignment operator
                         * Return a copy of this Iterator, after modification
                         */
                        Iterator& operator=(const Iterator& other)
                        {
                            head_ = other.head_;
                            tail_ = other.tail_;
                            cursor_ = other.cursor_;
                            return *this;
                        }
                        
                        /// Comparison operator
                        bool operator==(const Iterator& other) { return cursor_== other.cursor_; }
                        
                        /// Inequality comparison operator
                        bool operator!=(const Iterator& other) { return !((*this) == other);}
                        
                        /**
                         * Prefix increment operator
                         * Return a copy of this Iterator, after modification
                         */
                        Iterator& operator++()
                        {
                            //  TODO: Your code here
                            if(cursor_ != nullptr) {
                                cursor_ = cursor_ -> getNext();
                            }
                            return *this;
                        }
                        
                        /**
                         * Postfix increment
                         * Return a copy of this Iterator, BEFORE it was modified
                         */
                        Iterator operator++(int)
                        {
                            Iterator temp = *this;
                            if(cursor_ != nullptr) {
                                cursor_ = cursor_ -> getNext();
                            }
                            return temp;
                        }
                        
                        /**
                         * Prefix decrement operator
                         * Return a copy of this Iterator, after modification
                         */
                        Iterator& operator--()
                        {
                            //  TODO: Your code here
                            if(cursor_ != nullptr) {
                                cursor_ = cursor_ -> getPrev();
                            }
                            return *this;
                        }
                        
                        /**
                         * Postfix decrement operator
                         * Return a copy of this Iterator BEFORE it was modified
                         */
                        Iterator operator--(int)
                        {
                            //  TODO: Your code here
                        
                            if(cursor_ != nullptr) {
                                Iterator temp (head_, tail_, cursor_);
                                cursor_ = cursor_ -> getPrev();
                                return temp;
                            }
                            return *this;
                        }
                        
                        /**
                         * AdditionAssignment operator
                         * Return a copy of the current iterator, after modification
                        */
                        Iterator operator +=(size_t add)
                        {
                            //  TODO: Your code here
                            cursor_ += add;
                            return *this;
                        }
                        /**
                         * SubtractionAssignment operator
                         * Return a copy of the current iterator, after modification
                         */
                        Iterator operator -=(size_t add)
                        {
                            //  TODO: Your code here
                            cursor_ -= add;
                            return *this;
                        }
                        
                        /**
                         * AdditionAssignment operator, supporting positive or negative ints
                         */
                        Iterator operator +=(int add)
                        {
                            //  TODO: Your code here
                            while (cursor_ != nullptr && add != 0) {
                                if(add > 0) {
                                    for(int i = 0; i < add; i++)
                                    ++(*this);
                                } else if (add < 0) {
                                    --(*this);
                                }
                            }
                            return *this;
                        }
                        
                        /**
                         * SubtractionAssignment operator, supporting positive or negative ints
                         */
                        Iterator operator -=(int subtract)
                        {
                            //  TODO: Your code here
                            while(cursor_ != nullptr && subtract != 0) {
                                if(subtract > 0) {
                                    for(int i = 0; i < subtract; i++)
                                    --(*this);
                                } else if(subtract < 0) {
                                    for(int i = 0; i < -subtract; i++) {
                                        ++(*this);
                                    }
                                }
                            }
                            return *this;
                        }
                        
                        /**
                         * Dereference operator returns a reference to the ELEMENT contained with the current node
                         */
                        T& operator*()
                        {
                            //  TODO: Your code here
                            if(cursor_ == nullptr) {
                                throw std::range_error ("Invalid cursor");
                            }
                            return cursor_ -> getElement();
                        }
                    
                    private:
                        
                        /// Pointer to the head node
                        Node* head_ = nullptr;
                        
                        /// Pointer to the tail node
                        Node* tail_ = nullptr;
                        
                        /**
                         * Pointer to the cursor node.
                         * This is only one way of letting the iterator traverse the linked list.
                         * You can change to a different method if you wish (and can still pass unit tests)
                         */
                        Node* cursor_ = nullptr;
                    
                    friend class DoublyLinkedList;
                };
                
                /// Your welcome
                DoublyLinkedList()
                {
                    //  TODO: Your code here
                }
                
                /// Copy Constructor
                DoublyLinkedList(DoublyLinkedList& other)
                {
                    //  TODO: Your code here
                    for(auto it = other.begin(); it != other.end(); ++it) {
                        push_back(*it);
                    }

                }
                
                /// DTOR
                ~DoublyLinkedList()
                {
                    //  TODO: Your code here
                    clear();
                }
                
                /**
                 * Clear the list and assign the same value, count times.
                 * If count was 5, T was int, and value was 3,
                 *  we'd end up with a list like {3, 3, 3, 3, 3}
                 */
                void assign(size_t count, const T& value)
                {
                    //  TODO: Your code here
                    clear();
                    for(size_t i = 0; i < count; ++i) {
                        push_back(value);
                    }
                }
                
                /**
                 * Clear the list and assign values from another list.
                 * The 'first' iterator points to the first item copied from the other list.
                 * The 'last' iterator points to the last item copied from the other list.
                 * 
                 * Example:
                 *  Suppose we have a source list like {8, 4, 3, 2, 7, 1}
                 *  Suppose first points to the 4
                 *  Suppose last points to the 7
                 *  We should end up with our list becoming: {4, 3, 2, 7}
                 *
                 * If the user code sends out-of-order iterators,
                 *  just copy from 'first' to the end of the source list
                 * Example: first=7, last=4 from the list above would give us:
                 *  {7, 1}
                 */
                void assign(Iterator first, Iterator last)
                {
                    clear();

                    for(auto it = first; it != last; ++it) {
                        push_back(*it);
                    }

                }
                
                /// Return a pointer to the head node, if any
                Node* head() const
                {
                    return head_;
                }
                
                /// Return a pointer to the tail node, if any
                Node* tail() const
                {
                    return tail_;
                }
                
                /**
                 * Return an iterator that points to the head of our list
                 */
                Iterator begin() const
                {
                    //  TODO: Your code here
                    
                    return Iterator(head_, tail_, head_);
                }
                
                /**
                 * Return an iterator that points to the last element (tail) of our list
                 */
                Iterator last() const
                {
                    //  TODO: Your code here
                    
                    return Iterator(head_, tail_, tail_);
                }
                
                /**
                 * Should return an iterator that represents being past the end of our nodes,
                 * or just that we are finished.
                 * You can make this a nullptr or use some other scheme of your choosing,
                 *  as long as it works with the logic of the rest of your implementations.
                 */
                Iterator end() const
                {
                    //  TODO: Your code here
                    
                    return Iterator(nullptr, nullptr, nullptr);
                }
                
                /**
                 * Returns true if our list is empty
                 */
                bool empty() const
                {
                    //  TODO: Your code here
                    return head_ == nullptr;
                }
                
                /**
                 * Returns the current size of the list
                 * Should finish in constant time!
                 * (keep track of the size elsewhere)
                 */
                size_t size() const
                {
                    //  TODO: Your code here
                    return size_;
                }
                
                /**
                 * Clears our entire list, making it empty
                 * Remember: All removal operations should be memory-leak free.
                 */
                void clear()
                {
                    //  TODO: Your code here
                    //Node* current = head_;
                    Node* current = head_;
                    while(current != nullptr) {
                        Node* nextOne = current -> getNext();
                        delete current;
                        current = nextOne;
                        size_ --;
                    }
                    head_ = nullptr;
                    tail_ = nullptr;
                }
                
                /**
                 * Insert an element after the node pointed to by the pos Iterator
                 * 
                 * If the list is currently empty,
                 *  ignore the iterator and just make the new node at the head/tail (list of length 1).
                 * 
                 * If the incoming iterator is this->end(), insert the element at the tail
                 * 
                 * Should return an iterator that points to the newly added node
                 * 
                 * To avoid repeated code, it might be a good idea to have other methods
                 *  rely on this one.
                 */
                Iterator insert_after(Iterator pos, const T& value)
                {
                    //  TODO: Your code here
                    
                    //
                    return Iterator();
                }
                
                /**
                 * Insert a new element after the index pos.
                 * Should work with an empty list.
                 * 
                 * Should return an iterator pointing to the newly created node
                 * 
                 * To reduce repeated code, you may want to simply find
                 *  an iterator to the node at the pos index, then
                 *  send it to the other overload of this method.
                */
                Iterator insert_after(size_t pos, const T& value)
                {
                    //  TODO: Your code here
                    
                    return Iterator();
                }
                
                /**
                 * Erase the node pointed to by the Iterator's cursor.
                 * 
                 * If the 'pos' iterator does not point to a valid node,
                 *  throw an std::range_error
                 * 
                 * Return an iterator to the node AFTER the one we erased,
                 *  or this->end() if we just erased the tail
                 */
                Iterator erase(Iterator pos)
                {
                    //  TODO: Your code here
                    
                    return Iterator();
                }
                
                /**
                 * Add an element just after the one pointed to by the 'pos' iterator
                 * 
                 * Should return an iterator pointing to the newly created node
                 */
                Iterator push_after(Iterator pos, const T& value)
                {
                    //  TODO: Your code here
                    
                    //
                    return Iterator();
                }
                
                /**
                 * Add a new element to the front of our list.
                 */
                void push_front(const T& value)
                {
                    //  TODO: Your code here
                }
                
                /**
                 * Add an element to the end of this list.
                 * 
                 * Should return an iterator pointing to the newly created node.
                 */
                Iterator push_back(const T& value)
                {
                    //TODO: Your code here
                    Node* newNode = new Node(value);
                    if(empty()) {
                        head_ = newNode;
                        tail_ = newNode;
                    } 
                    newNode -> setPrev(tail_);
                    tail_ -> setNext(newNode);
                    tail_ = newNode;
                    
                    size_++;
                    return last();
                }
                /**
                 * Remove the node at the front of our list
                 * 
                 * Should throw an exception if our list is empty
                 */
                void pop_front()
                {
                    //  TODO: Your code here
                }
                
                /**
                 * Remove the node at the end of our list
                 * 
                 * Should throw an exception if our list is empty
                 */
                void pop_back()
                {
                    //  TODO: Your code here
                }
                
                /**
                 * Return a reference to the element at the front.
                 * 
                 * Throw an exception if the list is empty
                 */
                T& front()
                {
                    //  TODO: Your code here
                    if(empty()) {
                        throw std::range_error("The list is empty");
                    }
                    return head_->getElement();
                }
                
                /**
                 * Return a reference to the element at the back.
                 * 
                 * Throw an exception if the list is empty
                 */
                T& back()
                {
                    //  TODO: Your code here
                    if(empty()) {
                        throw std::range_error("The list is empty");
                    }
                    return tail_->getElement();
                }
                
                /**
                 * Return the element at an index
                 * 
                 * Should throw a range_error is out of bounds
                 */
                T& at(size_t index)
                {
                    //  TODO: Your code here
                    if(empty()) {
                        throw std::range_error("Empty list");
                    }
                    else if(index >= size_ ) {
                        throw std::range_error("Index is out of bounds");
                    }
                    Node* current = head_;
                    size_t i = 0;
                    while(i < index) {
                        current = current->getNext();
                        i++;
                    }
                    return current->getElement();
                }
                
                /**
                 * Reverse the current list
                 * 
                 * It might be easy to consider the following:
                 * - Create a temp empty list
                 * - Iterate through the current list
                 * - For each item in the current list, push to the FRONT (not back)
                 * - Assign the current list to the temp list
                 * - Discard the temp list
                 */
                void reverse()
                {
                    //  TODO: Your code here
                }
                
                /**
                 * I bet you're happy I'm not making you do this.
                 * No tests will be run against this function,
                 *  but feel free to try it out, as a challenge!
                 * 
                 * If I were doing this and didn't care too much for efficiency,
                 *  I would probably create an extra helper function to swap two
                 *  positions in the current list.
                 * Then I would simply sweep through the list and perform
                 *  the bubble-sort algorithm. Perhaps selection sort.
                 * 
                 * If you want a huge challenge, try implementing quicksort.
                 * 
                 * (but again, don't worry about this method; it will not be tested)
                 */
                void sort()
                {
                    //  TODO: Your code here
                }
                
                /**
                 * Assignment operator
                 * 
                 * Clear this list and fill it with the others' values
                 * (by value, not by reference)
                 * 
                 * Return a reference to this list
                 */
                DoublyLinkedList<T>& operator =(DoublyLinkedList<T>& other)
                {
                    //  TODO: Your code here
                    return *this;
                }
                
                /**
                 * Return true if the lists are "equal"
                 * 
                 * "Equal" here is defined as:
                 * - Same size
                 * - Elements at the same indexes would return true for their own comparison operators
                 * 
                 * In other words: "They contain all the same values"
                 * (no need to be references to each other)
                 */
                bool operator ==(DoublyLinkedList<T>& other)
                {
                    //  TODO: Your code here
                    if(size() != other.size()) {
                        return false;
                        throw std::range_error("The size is not the same");
                    }
                    Iterator head = begin();
                    Iterator otherhead = other.begin();
                    
                    while(head != end()) {
                        if(*head != *otherhead) {
                            return false;
                        }
                        ++head;
                        ++otherhead;
                    }
                    return true;
                }
                
                /**
                 * Return true if the lists are "not equal"
                 * 
                 * See the operator== stub for definition of "equal"
                 * 
                 * Probably want to avoid repeated code by relying on the other operator
                 */
                bool operator !=(DoublyLinkedList<T>& other)
                {
                    //  TODO: Your code here
                    
                    return !(*this == other);
                }
                
            private:
                
                Node* head_ = nullptr;
                Node* tail_ = nullptr;
                size_t size_ = 0;
        };
    }
}

#endif

当我在分配()中注释掉push_back函数时,测试可以运行,但是一旦我使用push_back来分配值,我就得到了中止(核心转储)错误,

c++ iterator doubly-linked-list
1个回答
0
投票

原因是

push_back
方法。您正确测试列表最初是否为空,但在为空时继续按顺序:

        Iterator push_back(const T& value)
        {
            //TODO: Your code here
            Node* newNode = new Node(value);
            if (empty()) {
                head_ = newNode;
                tail_ = newNode;
            }
            newNode->setPrev(tail_); // if empty tail_ IS newNode !!!
            tail_->setNext(newNode);
            tail_ = newNode;

            size_++;
            return last();
        }

您应该使用

else
作为备用分支:

        Iterator push_back(const T& value)
        {
            //TODO: Your code here
            Node* newNode = new Node(value);
            if (empty()) {
                head_ = newNode;
                tail_ = newNode;
            }
            else {
                newNode->setPrev(tail_);
                tail_->setNext(newNode);
                tail_ = newNode;
            }
            size_++;   // hoping size is set to 0 for an empty list...
            return last();
        }

但是注意:由于您的代码远非最小的可重现示例,我无法测试并且可能存在其他问题......

© www.soinside.com 2019 - 2024. All rights reserved.