Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linked-list in C++ using references instead of pointers

Suppose I want to create an unmodifiable linked-list (i.e. it can only be traversed, no nodes can be added or removed once it was initially created). This could be easily implemented by:

struct ListNode
{
  int value;
  ListNode* nextNode;
}

My question is .... Would it be possible to use references instead of pointers?

struct ListNodeWithRefs
{
  int value;
  ListNodeWithRefs &nextNode;
}

I am not sure it would provide any performance gain at all but ... this question popped up while coding and my answer so far is no but I could be missing something.

In principle, nothing prevents you from using references, and constructing list elments like this:

ListNodeWithRefs::ListNodeWithRefs(ListNodeWithRefs &next):
  nextNode(next)
{}

But there is a chicken and egg problem because next also enforces its next element to exist at its creation and so on ...

Note: I think my question can also be applied to defining the list as:

struct ListNodeConst
{
  int value;
  const ListNode* nextNode;
}
like image 816
fons Avatar asked Mar 03 '13 13:03

fons


People also ask

Why are references better than pointers?

References are used to refer an existing variable in another name whereas pointers are used to store address of variable. References cannot have a null value assigned but pointer can. A reference variable can be referenced by pass by value whereas a pointer can be referenced but pass by reference.

Do linked lists use pointers?

A linked list is a list constructed using pointers. A linked list is not fixed in size but can grow and shrink while your program is running. This chapter shows you how to define and manipulate linked lists, which will serve to introduce you to a new way of using pointers.


1 Answers

This is typical of a cons-list in functional languages:

data List a = Empty | Node a (List a)

The trick is though, List a is a full type and can refer either to Empty OR another node (which is why it can terminate).

In order to achieve this in C++, you could take advantage of either a union (but it's not that well supported) or of polymorphism.

template <typename T>
struct ListBase {
    enum class Kind { Empty, Node };
    explicit ListBase(Kind k): _kind(k) {}

    Kind _kind;
};

And then:

template <typename T>
struct EmptyList: ListBase<T> {
    EmptyList(): ListBase<T>(Kind::Empty) {}
};

template <typename T>
struct ListNode: ListBase<T> {
    ListNode(T const& t, ListBase<T>& next):
        ListBase<T>(Kind::Node), _value(t), _next(next) {}

    T _value;
    ListBase<T>& _next;
};

And now, you don't have a chicken & egg problem any longer; just start from an instantiation of EmptyList<T>.

Note: the presence of _kind in the base class is not that OO, but it makes things closer to the functional example by tagging which alternative is used.

like image 56
Matthieu M. Avatar answered Nov 03 '22 00:11

Matthieu M.