LinkedList Class Linked List • Member functions: add, clear, get, insert, isEmpty, remove, size, toString • LinkedList instance variables: • pointer to list, i.e., pointer to front node • front is NULL if list is empty • int data value LinkedList front add(value) insert(index, value) remove(index) size() ListNode 13 13 13 NULL ListNode struct (LinkedList.h) #ifndef _LINKEDLIST_H #define _LINKEDLIST_H struct ListNode { int data; ListNode* next; ListNode(int d = 0, ListNode* ptr = NULL) { data = d; next = ptr; } }; LinkedList Class (LinkedList.h) class LinkedList { public: LinkedList(); ~LinkedList(); // destructor void add(int value); void clear(); int get(int index) const; void insert(int index, int value); bool isEmpty() const; void remove(int index); void set(int index, int value); int size() const; friend ostream& operator <<(ostream& out, LinkedList& list); private: ListNode * front; }; #endif LinkedList Class (LinkedList.cpp) // Construct a new empty list LinkedList::LinkedList() { front = NULL; } add Method // Add new value to the end of the list void LinkedList::add(int value) { if(front == NULL) { front = new ListNode(value); } else { ListNode* cur = front; while(cur next != NULL) cur = curnext; cur->next = new ListNode(value); } } get Method // Returns value at given index in list (first node at index 0). // Throws integer exception if specified index is not in the interval [0,size-1] int LinkedList::get(int index) const { if(index < 0 || index >= size()) throw index; ListNode* cur = front; for(int i = 0; i < index; i++) cur = cur->next; return cur->data; } Exercise: // Sets value in ith node in the list to specified value. // Throws integer exception if specified index is not in interval [0, size-1]. void LinkedList::set(int index, int value) { insert Method // Insert specified value at specified index in list. void LinkedList::insert(int index, int value) { if(index < 0 || index >= size()) throw index; if(index == 0) { ListNode* list = front; front = new ListNode(value); front next = list; } else { ListNode* cur = front; for(int i = 0; i < index-1; i++) cur = cur next; // add new node after cur ListNode* newNode = new ListNode(value); newNode next = cur next; cur next = newNode; } } remove Method // Remove value at specified index from the list. void LinkedList::remove(int index) { if(index < 0 || index >= size()) throw index; ListNode *temp; if(index == 0) { temp = front; front = front next; } else { ListNode* cur = front; for(int i = 0; i < index-1; i++) cur = cur next; temp = curnext; cur next = temp next; } delete temp; } clear Method // Remove all values from the list. void LinkedList::clear() { while(front != NULL) remove(0); } Destructor • Destructor should free all nodes in the list LinkedList::~LinkedList() { clear(); } • A default constructor is provided. But it will not delete the nodes in the linked list that front points to. Destructors, Copy Constructors & Copy Assignment Operators Non-Default Destructor When do you need it? • Memory is allocated in your constructor: use a destructor to delete it. • Otherwise: memory leaks Copy Constructor & Copy Assignment Operator • Two ways to copy an object: • copy constructor: create a new object that's a copy of an existing object • copy assignment operator: set an existing object equal to another object • For existing Person object p: • Copy constructor: • Person p2(p); // create new object p2 using state of existing object p • Or equivalently: Person p2 = p; • Copy assignment operator: • Person p2 = ...; • p2 = p; // p2 is a Person object, and we want to set its state equal to that of p • If we don't define them in our classes, we get a default destructor, copy constructor and copy assignment operator. • When do we need to define our own? If an object has pointers or some other runtime allocation of a resource (e.g., opening a file). • default copy constructor and assignment operator will do a shallow copy • default destructor will not deallocate memory allocated for an instance variable Example class String { private: char* str; int length; public: String(const char* str = NULL); // constructor ~String(); String(const String&); // copy constructor // other member functions... }; Shallow Copy • Default assignment operator and copy constructor produce a shallow copy String s1("hello"); String s2 = s1; // default copy constructor invoked s1 s2 str length str length 5 'h' 'e' 'l' 'l' 5 'o' '\0' Default Destructor • Default destructor does not free the heap-allocated array that the str instance variable points to String* s1 = new String("hello"); delete s1; // default destructor results in memory leak s1 str length 5 'h' 'e' 'l' 'l' 'o' '\0' Example String::String(const char *s) { length = strlen(s); str = new char[length+1]; strcpy(str, s); } String::~String() { delete [] str; } // copy assignment operator String& String::operator =(const String &s){ // copy constructor if(this != &s) { delete[] str; String::String(const String& oldStr){ length = s.length; length = oldStr.length; str = new char[length]; str = new char[length+1]; strcpy(str, s.str); strcpy(str, oldStr.str); } } return *this; } Copy Constructor & Copy Assignment • To avoid implementing them, you can declare them to be private in your class. • Then if a client attempts to invoke them, they get a compile error. • The Rule of Three is a rule of thumb for C++: if your class needs to define any of copy constructor, assignment operator, destructor, then it needs all three of them. Exercise • Add copy constructor and copy assignment operator to LinkedList class