Encapsulation

When we keep both head and tail pointers, a list no longer consists of a single object of type Node*. Instead, it consists of two objects, local variables head and tail. Whenever we see that a single entity in a program consists of several local variables like this, we should begin to think about wrapping them up in a struct.

struct List {
  Node *head, *tail;
};

With this declaration, a list in our program is an object of this new type "List", and functions in our library start to look like this:

List initialize() {
  List L;
  L.head=NULL;
  L.tail=NULL;
  return L;
}

List add2Front(string val, List L) {
  Node* p = new Node;
  p->data = val;
  p->next = L.head;
  if (L.head == NULL){
    L.head = p;
    L.tail = p;
  } else
    L.head = p;
  return L;
}

List add2Back(string val, List L) {
  Node* p = new Node;
  p->data = val;
  p->next = NULL;
  if (L.head == NULL) {
    L.head = p;
    L.tail = p;
  } else {
    L.tail->next = p;
    L.tail = p;
  }
  return L;
}

ostream& operator<<(ostream& OUT, List L) {
  for (Node* t = L.head; t != NULL; t=t->next)
    OUT << t->data << ' ';
  return OUT;
}

and then our main might look like this:

int main() {
  List L = initialize();
  cout << "Enter ints, end with 0: ";
  int temp;
  while(cin >> temp && temp > 0)
    L=add2back(temp,L);
  cout << "you entered: " << L << endl;
  return 0;
}

Why would you do such a thing? Think about if you're providing this library to a user who isn't you. They have no interest in how you implemented this. Did you name them Nodes, or maybe Link? Did you name the pointers head and tail, or first and last? They don't care, and they're not particularly interested in digging through your code to find out.

The goal of proper encapsulation is to set up your library so the user doesn't have to know any of your implementation details, and can use your library entirely by looking at your interface, as detailed in your .h file.

In this instance, the .h file might look like this:

/* A library for linked list functions for integers */
#ifndef LINKEDLISTHEADER
#define LINKEDLISTHEADER

#include "Node.h"
#include <iostream>
using namespace std;

struct List {
  Node *head, *tail;
};

List initialize(); //builds and returns an empty list
List add2Front(int val, List l); //adds val to the front of List l and returns
                                 //the resulting List
List add2Back(int val, List l); //adds val to the back of List l and returns
                                //the resulting List
ostream& operator<<(ostream& OUT, List l); //prints out the List in order from
                                           //first to last
#endif

With this library (and more functions), your user can do all sorts of linked list stuff without knowledge of even what's in a Node or how these functions work - which is good, because she doesn't care!