CS107L Handout 04 Autumn 2007 October 19, 2007 Custom STL-Like Containers and Iterators



Similar documents
CS107L Handout 02 Autumn 2007 October 5, 2007 Copy Constructor and operator=

CS193D Handout 06 Winter 2004 January 26, 2004 Copy Constructor and operator=

CS104: Data Structures and Object-Oriented Design (Fall 2013) October 24, 2013: Priority Queues Scribes: CS 104 Teaching Team

C++ INTERVIEW QUESTIONS

Object Oriented Software Design II

N3458: Simple Database Integration in C++11

C++ Overloading, Constructors, Assignment operator

Integrating the C++ Standard Template Library Into the Undergraduate Computer Science Curriculum

Comp151. Definitions & Declarations

Lecture Notes on Binary Search Trees

A binary search tree or BST is a binary tree that is either empty or in which the data element of each node has a key, and:

For the next three questions, consider the class declaration: Member function implementations put inline to save space.

Boolean Expressions, Conditions, Loops, and Enumerations. Precedence Rules (from highest to lowest priority)

C++FA 3.1 OPTIMIZING C++

Lecture 12 Doubly Linked Lists (with Recursion)

CSI33 Data Structures

Data Structures Using C++ 2E. Chapter 5 Linked Lists

1) The postfix expression for the infix expression A+B*(C+D)/F+D*E is ABCD+*F/DE*++

Friendship and Encapsulation in C++

Introduction to C++ Introduction to C++ Week 7 Dr Alex Martin 2013 Slide 1

Why you shouldn't use set (and what you should use instead) Matt Austern

Linked Lists: Implementation Sequences in the C++ STL

Node-Based Structures Linked Lists: Implementation

Lecture Notes on Binary Search Trees

1 Abstract Data Types Information Hiding

Common Data Structures

Lecture 11 Doubly Linked Lists & Array of Linked Lists. Doubly Linked Lists

First programming project: Key-Word indexer

Compile-time type versus run-time type. Consider the parameter to this function:

IS0020 Program Design and Software Tools Midterm, Feb 24, Instruction

C++ Programming Language

GDB Tutorial. A Walkthrough with Examples. CMSC Spring Last modified March 22, GDB Tutorial

Moving from CS 61A Scheme to CS 61B Java

10CS35: Data Structures Using C

Data Structures Using C++ 2E. Chapter 5 Linked Lists

Math 4310 Handout - Quotient Vector Spaces

Sequences in the C++ STL

ADTs,, Arrays, Linked Lists

NTFS permissions represent a core part of Windows s security system. Using

Copyright 2001, Bill Trudell. Permission is granted to copy for the PLoP 2001 conference. All other rights reserved.

1. Polymorphism in C++...2

Linked List as an ADT (cont d.)

How to create PDF maps, pdf layer maps and pdf maps with attributes using ArcGIS. Lynne W Fielding, GISP Town of Westwood

C++FA 5.1 PRACTICE MID-TERM EXAM

Cours de C++ Utilisations des conteneurs

C++ Crash Kurs. C++ Object-Oriented Programming

16 Collection Classes

PART-A Questions. 2. How does an enumerated statement differ from a typedef statement?

Decision Making under Uncertainty

Where's Gone? LEAD GENERATION PRINTABLE WORKBOOK

Short Notes on Dynamic Memory Allocation, Pointer and Data Structure

Chapter 3: Restricted Structures Page 1

Writing Thesis Defense Papers

0.8 Rational Expressions and Equations

RUP iteration planning

Variable Base Interface

Name: Class: Date: 9. The compiler ignores all comments they are there strictly for the convenience of anyone reading the program.

Creating and Managing Shared Folders

Software Engineering Concepts: Testing. Pointers & Dynamic Allocation. CS 311 Data Structures and Algorithms Lecture Slides Monday, September 14, 2009

Cpt S 223. School of EECS, WSU

CpSc212 Goddard Notes Chapter 6. Yet More on Classes. We discuss the problems of comparing, copying, passing, outputting, and destructing

Symbol Tables. Introduction

What you should know about: Windows 7. What s changed? Why does it matter to me? Do I have to upgrade? Tim Wakeling

Operator Overloading. Lecture 8. Operator Overloading. Running Example: Complex Numbers. Syntax. What can be overloaded. Syntax -- First Example

Coding Rules. Encoding the type of a function into the name (so-called Hungarian notation) is forbidden - it only confuses the programmer.

Object Oriented Software Design II

Lab 4.4 Secret Messages: Indexing, Arrays, and Iteration

Syntax Check of Embedded SQL in C++ with Proto

vector vec double # in # cl in ude <s ude tdexcept> tdexcept> // std::ou std t_of ::ou _range t_of class class V Vector { ector {

Sources: On the Web: Slides will be available on:

Basics of C++ and object orientation in OpenFOAM

Debugging. Common Semantic Errors ESE112. Java Library. It is highly unlikely that you will write code that will work on the first go

Lecture 3. Arrays. Name of array. c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9] c[10] c[11] Position number of the element within array c

Not agree with bug 3, precision actually was. 8,5 not set in the code. Not agree with bug 3, precision actually was

CORBA Programming with TAOX11. The C++11 CORBA Implementation

It starts like this...

Pre-Algebra Lecture 6

BBC Learning English Talk about English Business Language To Go Part 1 - Interviews

McKinsey Problem Solving Test Top Tips

CmpSci 187: Programming with Data Structures Spring 2015

Everything you wanted to know about using Hexadecimal and Octal Numbers in Visual Basic 6


Social Return on Investment

recursion, O(n), linked lists 6/14

Slave Computer 1 Slave Computer 2

WRITING PROOFS. Christopher Heil Georgia Institute of Technology

Ordered Lists and Binary Trees

The Doctor-Patient Relationship

50 Tough Interview Questions

Getting Started with WebSite Tonight

CS11 Advanced C++ Spring Lecture 9 (!!!)

Stacks. Linear data structures

The Top 3 Common Mistakes Men Make That Blow All Their Chances of Getting Their Ex-Girlfriend Back Which of these mistakes are you making?

The Little Go Book is licensed under the Attribution-NonCommercial-ShareAlike 4.0 International license. You should not have paid for this book.

Curriculum Map. Discipline: Computer Science Course: C++

KITES TECHNOLOGY COURSE MODULE (C, C++, DS)

MATLAB Functions. function [Out_1,Out_2,,Out_N] = function_name(in_1,in_2,,in_m)

FIELD GUIDE TO LEAN EXPERIMENTS

Unordered Linked Lists

Transcription:

CS107L Handout 04 Autumn 2007 October 19, 2007 Custom STL-Like Containers and Iterators This handout is designed to provide a better understanding of how one should write template code and architect iterators to traverse containers. The entire discussion centers on the design and implementation of a singly linked list something that is officially included in the STL. However, we rewrite a subset of that functionality here, because the linked list is the least complicated class of all classes requiring custom iterator implementations. Let s get to it. Design of mylist to look and feel like the STL list class If we re going to have a linked list, then there s no denying the need for a link list node. class mylist; class mylist_iterator; // forward declare // forward declare class mylist_node friend class mylist<t>; friend class mylist_iterator<t>; mylist_node(const T& t, mylist_node<t> *next) : elem(t), next(next) ~mylist_node() delete next; T elem; mylist_node<t> *next; ; If, for just a moment, you rid of the constructor, you reduce the mylist_node to nothing more than a normal struct that just happens to mark everything as private. Naturally, to mark everything as private is to block everyone out, and for the most part, that s what we want here. However, mylist, mylist_node, and mylist_iterator are clearly intertwined, so the mylist_node<t> class allows any instance of mylist<t> and mylist_iterator<t> to examine the private data of a mylist_node<t> instance. This permission comes in the form of an explicit friendship statement placed at the front of the mylist_node definition.

2 Some key (or at least interesting) observations: 1. The public and private keywords have absolutely no influence over friendship. The very fact that the mylist_node granted friendship is enough to give all mylist and mylist_iterator full access to everything mylist_node-related. Note that the friendship is being offered to the classes themselves. That means that all instance and class methods have full access to any instance field of the mylist_node specifically, mylist and mylist_iterators can directly access any and all private data members and, had there been any, call any private methods as well. 2. Understand that the friendship is being granted to classes embracing the same exact type. mylist_node<string> grants friendship to mylist<string>, mylist_node<film *> grants friendship to mylist<film *>, and so forth. Technically, mylist_node<string> couldn t give a flying feather about mylist<film *>, so it sees no reason to grant friendship to all things mylist just mylist<string>. This should be clear, because the lines granting friendship include the <T>. friend class mylist<t>; friend class mylist_iterator<t>; 3. I chose to inline the implementation of the constructor and destructor, but I m only getting away with it here because there are so short and so relatively obvious. Had I preferred to place the implementations in the corresponding.cc file instead, I d have to have written them as follows: mylist_node<t>::mylist_node(const T& t, mylist_node<t> *next) : elem(t), next(next) // nothing needed, as everything is taken care of by the initialization list mylist_node<t>::~mylist_node() delete next; 4. Remember that the compiler automatically generates a copy constructor and an operator= method for us if we don t explicitly mention it in the class definition. Since we don t mention either here, we get those compilersynthesized versions (and they re automatically public).

Who would have thought that the mylist_node template class could be so very interesting? Let s start small and pretend that anything and everything relying on the existence of a true iterator type isn t included. class mylist mylist() : head(null), tail(null) ~mylist() delete head; bool empty() const return head == NULL; void push_back(const T& elem); mylist_node<t> *head, *tail; ; This doesn t even come close to the real linked list, but my ultimate goal here isn t to remind you what a linked list should do and how it works, but rather to motivate and implement an iterator. The mechanics of threading together a linked list is either old hat by now, or if not, can be reviewed in a matter of 15 minutes. The only difference between our linked list and the ones you ve dealt with in previous courses is the language we re building them in. The logistics of next pointers and NULL checks and whatnot are exactly the same. Notice I ve inlined the implementation of the constructor, the destructor, and the empty method. Here s what the mylist.cc file would look like if this were all mylist was defined to be. void mylist<t>::push_back(const T& elem) mylist_node<t> *newnode = new mylist_node<t>(elem, NULL); if (head == NULL) head = newnode; else tail->next = newnode; tail = newnode; The details of linked list insertion and deletion are always tricky, no matter what language they re in. If you doubt the implementation, then you should trace each method to ensure that all cases (empty list, list of length one, and all other lists) are properly handled. The most interesting element of the implementation at least from a C++ standpoint is the call to the mylist_node<t> constructor a call that s permitted only because the mylist class was granted that friendship mentioned earlier. Now that we have the basics of the linked list in place, it s high time we introduce the iterator so that built-in STL algorithms like for_each and find can traverse the elements of our mylist from front to back and making it appear as if the elements 3

inside the mylist are all laid out sequentially in memory. A terribly bad design would extend our current definition of the mylist class to include very buggy implementations of begin and end methods. Buggy, buggy, buggy, buggy! class mylist typedef T * iterator ; mylist() : head(null), tail(null) ~mylist(); bool empty() const return (head == NULL); void push_back(const T& elem); iterator begin () return (head == NULL? NULL : &head->elem); iterator end () return NULL; 4 ; mylist_node<t> *head, *tail; Such an implementation would assume that we ve absolutely no other choice for the typedef. If begin needs to return the 'address' of the first element and this 'address' should respond to * and ++ as a true pointer would, one might think there s really nothing else we can do. Perhaps it s the best that can be done; but if so, then this whole iterator thing would be pretty lame particularly lame here, since there s no way the individual elements of a list could possibly be accessed by an iterator that assumes all elements are organized side by side in memory. To drive this point home, consider the following: mylist<string> ivies; ivies.push_back("harvard"); ivies.push_back("yale"); ivies.push_back("princeton"); ivies.push_back("penn"); mylist<string>::iterator begin = ivies.begin(); mylist<string>::iterator end = ivies.end(); while (begin!= end) if (*begin == "Stanford") cout << "Stanford s an Ivy." << endl; return; ++begin; cout << "They re all snobs anyway." << endl;

Algorithmically, the code snippet is sound, and yet it doesn t work in fact, it seg faults. The blame can t be pinned on the code snippet itself, but rather the current definition of the mylist<string>::iterator. How can a local string * that s all the iterator is in this example behave any differently than a regular pointer? All those ++begin instructions are going to advance the pointer through memory as if there s an array of strings there, and that s just not the case. The iterator doesn t know how to advance to the next element in the sequence, because the instructions it follows to advance operator++ being that instruction just tell it to march sizeof(string) bytes ahead. The iterator we want here is something that knows how to update itself to point to the next element in the list. A normal pointer can t do that, but a class that responds to clever implementations of operator* and operator++ can. The idea of returning a class as an iterator seems a little off. However, as long as the type being returned responds to pointer syntax and exhibits normal pointer semantics, we shouldn t really care whether the iterator is a true pointer or something that just pretends to be. We re interested in appearances once again, it all comes down to looks. Shallow! 5 At this point, we can update the mylist template to export a new iterator type one that has a better chance of visiting all of the nodes: class mylist typedef mylist_iterator<t> iterator ; mylist() : head(null), tail(null) ~mylist() delete head; bool empty() const return head == NULL; void push_back(const T& elem); iterator begin(); iterator end(); ; mylist_node<t> *head, *tail; Notice the new typedef for iterator there s a big difference. begin and end now return an instance of this mylist_iterator<t> thing a type we ll more fully flesh out in a page or two.

While the details of how the mylist_iterator aren t clear yet, we do know that the code snippet we wrote earlier will now be translated/interpreted as follows: 6 mylist<string> ivies; ivies.push_back("harvard"); ivies.push_back("yale"); ivies.push_back("princeton"); ivies.push_back("penn"); what we write mylist<string>::iterator begin = ivies.begin(); mylist<string>::iterator end = ivies.end(); while (begin!= end) if (*begin == "Stanford") cout << "Stanford s an Ivy." << endl; return; ++begin; cout << "They re all snobs anyway." << endl; how the compiler interprets it when mylist<string>::iterator is a direct class instance. mylist<string> ivies; ivies.push_back("harvard"); ivies.push_back("yale"); ivies.push_back("princeton"); ivies.push_back("penn"); mylist<string>::iterator begin = ivies.begin(); mylist<string>::iterator end = ivies.end(); while ( begin.operator!=(end) ) if ( begin.operator*() == "Stanford") cout << "Stanford s an Ivy." << endl; return; begin.operator++() ; cout << "They re all snobs anyway." << endl; The placement of the new and improved mylist<string>::iterator in this context forces it to respond to operator!=, operator*, and operator++ in order to make the compiler happy. And because we want the iterator to traverse the mylist<string> and identify references to all of the strings inside, the implementations of operator!=, operator*, and operator++ need to mimic whatever functionality comes when these operators are applied to real pointers. Naturally, we also want the begin iterator to point to the first element of the list, the next iterator to somehow associate with the second element in the list, and so on.

7 Therefore, we need the following: class mylist_iterator : public iterator<forward_iterator_tag, T> friend class mylist<t>; T& operator*(); const mylist_iterator<t>& operator++(); bool operator!=(const mylist_iterator<t>& other) const; ; mylist_node<t> *pointee; mylist_iterator(mylist_node<t> *pointee) : pointee(pointee) T& mylist_iterator<t>::operator*() return pointee->elem; const mylist_iterator<t>& mylist_iterator<t>::operator++() assert(pointee!= NULL); pointee = pointee->next; return *this; bool mylist_iterator<t>:: operator!=(const mylist_iterator<t>& other) const return this->pointee!= other.pointee; Forget about public versus private for the moment; pay attention to what this mylist_iterator<t> class encapsulates and how it behaves like a pointer to an object of type T. Each instance of this mylist_iterator<t> class wraps around a pointer to a mylist_node<t>. operator++ doesn t advance the iterator to point to the next node in sequential memory, but instead to point to the next node in the list (note the update setting pointer to pointer->next). operator!= mismatches mylist_iterators if and only if the mylist_node<t> *s they surround are different. operator* returns a reference to the T object embedded inside the node whose address it s storing. You ll should make note of my decision to make all three operator methods public; marking them as private would prevent client code from handling iterators produced by the begin and end methods. See the private constructor? That means no one except the mylist<t> class can create mylist_iterator<t> s around a mylist_node<t> *. (The copy constructor, operator= method, and destructor are all public and compiler-synthesized, and the compiler-synthesized ones work just fine.)

8 The implementation of operator++ is such that the following idiom would carry the iterator over all of the mylist_node<t> *s of a mylist<t>. begin would need to create an iterator storing the very first address, and then operator++ would update the iterator to hold the next field, and then the next next field, and then the NEXT next field, and so on. Eventually, the iterator would be updated to store a NULL and match the iterator produced by the end method: That would be the signal that we ve reached the end of the list. Assuming this is all true, it makes the implementation of begin and end pretty obvious: class mylist typedef mylist_iterator<t> iterator; mylist() : head(null), tail(null) ~mylist() delete head; bool empty() const return head == NULL; void push_back(const T& elem); iterator begin() return mylist_iterator<t>(head); iterator end() return mylist_iterator<t>(null); ; mylist_node<t> *head, *tail; The Full mylist<t> Definition If you inspect the assn-7-ss-and-btmap starter folder, you ll see a subfolder name list-iterator-code. Within that directory is an even more robust definition and implementation of mylist. This mylist defines two find methods (one const, one non-const) and how you can use a static template method to unify their implementations implementations while are logically identical. It also shows you how to define the mylist_iterator template class so that both iterator and const_iterator can be supported. (Note that you are not required to implement true iterators for the btree_map. But you re certainly welcome and even encouraged to try.)