简体   繁体   中英

How to understand freelist implementation for linked-list in C++?

I don't understand the last few lines here.

// Singly linked list node with freelist support
template<typename Elem> class Link {
private:
  static Link<Elem>* freelist; // Reference to freelist head
public:
  Elem element;                // Value for this node
  Link* next;                  // Point to next node in list

  // Constructors
  Link(const Elem& elemval, Link* nextval = NULL)
    { element = elemval; next = nextval; }
  Link(Link* nextval = NULL) { next = nextval; }

  void* operator new(size_t) { // Overloaded new operator
    if (freelist == NULL) return ::new Link; // Create space
    Link<Elem>* temp = freelist; // Can take from freelist
    freelist = freelist->next;
    return temp;                 // Return the link
  }

  // Overloaded delete operator
  void operator delete(void* ptr) {
    ((Link<Elem>*)ptr)->next = freelist; // Put on freelist
    freelist = (Link<Elem>*)ptr;
  }
};

// The freelist head pointer is actually created here
template<typename Elem>
Link<Elem>* Link<Elem>::freelist = NULL;

In particular, why do we need to define a pointer to a pointer in the last line? Link<Elem>::freelist is already a pointer, so why do we need to set another pointer to that pointer to NULL ?

Can someone clear this up for me? I'm very confused.

Distilling your example down, this is what you have:

class Link {
    static Link* freelist;
}

Link* Link::freelist = NULL;

As the comment mentions, "The freelist head pointer is actually created here". The first part is a declaration of the static variable freelist but it does not actually define it. The definition below mimics the declaration (and gives it a value). It does not add another level of pointer.

Not only the last two lines should raise questions, but the implementation of the operator new .

Let's consider the following code:

struct S { 
    // ... 
};

auto list = new List<S>;

Here new List<S> will call List<S>::operator new to allocate raw memory. It will call global ::new , and that global ::new will allocate memory (with ::operator new ) and construct Link , ie S and Link* , in it. Then our local new will construct Link again at the same memory location. As a result, S will be constructed twice at the same memory location. Depending on S , this can lead to undefined behaviour and/or memory leaks.

Simple example:

struct S {
    S()  { std::cout << "ctor at " << this << '\n'; }
    ~S() { std::cout << "dtor at " << this << '\n'; }
};

int main() {
    auto list = new Link<S>;
    delete list;
}

Sample output:

ctor at 0x24b8e80
ctor at 0x24b8e80
dtor at 0x24b8e80

Declaration of static non-const member of class requires exactly one out-of-line definition. Static members do not use their class object's storage, so you have to declare and initialize its own separately.

class Link {
    static Link* freelist; // declaring that such object exists
};

// Shouldn't appear in a header file  to not break One Definition Rule
Link* Link::freelist = nullptr; // initialization

freelist is actually a "global" variable which could be accessed though context of class Link, eg Link::freelist if it was public. In this particular case , it's just being shared between all instances of class object.

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