45
Templates Templates Containers Containers Iterators Iterators (TIC++V1:C16) (TIC++V1:C16) Yingcai Xiao Yingcai Xiao 06/26/2008 06/26/2008

Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Embed Size (px)

Citation preview

Page 1: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

TemplatesTemplatesContainersContainersIteratorsIterators(TIC++V1:C16)(TIC++V1:C16)

Yingcai XiaoYingcai Xiao

06/26/200806/26/2008

Page 2: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Do you know?Do you know?

Why Java forces all classes subclass from the Why Java forces all classes subclass from the Object class?Object class?

How to implement an operator?How to implement an operator?How to create a generic container?How to create a generic container?

Object code reuseObject code reuseSource code reuseSource code reuse

FriendsFriendsEnum Enum

IteratorIterator

Page 3: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

class IntStack { static const int size = 100; // size has to be known at compile time for an array int stack[size]; int top;public: IntStack() : top(0) {} void push(int i) { stack[top++] = i; } int pop() { return stack[--top]; }};

Introduction to Templates : What’s wrong?

int main() { IntStack is;

for(int i = 0; i < 20; i++) is.push(i);

for(int k = 0; k < 20; k++) cout << is.pop() << endl;}

Example: StackInt.cpp

Page 4: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Problem #1: the stack could overflow. The problem is easy to solve. Just make the size of the stack variable. To do that we need to use dynamic memory allocation to increase the size of the stack if “top” reaches “size”. IntStack is a container class. All container class should not overflow, i.e., can contain as many number of items as needed.

Introduction to Templates

Page 5: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Problem #2: need to repeat the same code for float, double or any other types. Code reuse?

Can we use inheritance or composition to solve the problem? No, because they provide a way only to reuse object code. When we say “reuse object code” we mean “the size/type of the memory can’t be changed (even though the values in the memory can be changed)” .

Can we really reuse the same source code for different types? There are two solutions to the problem:

Object-based hierarchy in SmallTalk and JavaTemplate in C++ and Generic in C#

Introduction to Templates

Page 6: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Object-based Hierarchy

// In the library, to be reused code: a Stack that can store any “Object”.

class Object {public: virtual void Print() {cout << "I am an object. \n"; }

};

class Stack { static const int size = 100; Object* stack[size]; int top;public: Stack() : top(0) {} void push(Object* op) { stack[top++] = op; } Object* pop() { return stack[--top]; }};

Example: StackObjectBsaed.cpp

Page 7: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Object-based Hierarchy

// In an application, using the Stack for two different types of “Object”s.

class IntObject : public Object {int i;

public: IntObject(int i) { this->i = i; }void Print() {cout<<"Int value = " << i << endl;}

};

class FloatObject : public Object {float f;

public: FloatObject(float f) { this->f = f; }void Print() {cout<<"Float value = " << f << endl;}

};

Example: StackObjectBsaed.cpp

Page 8: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Object-based Hierarchy

// In an application, using the Stack for two different types of “Object”s.

int main() {Stack is;for(int i = 0; i < 8; i++) is.push(new IntObject(i));for(int k = 0; k < 8; k++) is.pop()->Print();

Stack fs;for(int i = 0; i < 8; i++) fs.push(new FloatObject((float)

(i+0.8)));for(int k = 0; k < 8; k++) fs.pop()->Print();

Stack s;for(int i = 0; i < 4; i++) {

s.push(new IntObject(i)); s.push(new FloatObject((float)(i+0.8)));

}for(int k = 0; k < 8; k++) s.pop()->Print();

} Example: StackObjectBsaed.cpp

Page 9: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

The container can contain any types of objects as long as the types are subclassed from the Object class directly or indirectly. The root of the hierarchy of classes is the Object class. The container stores references to Object. When an object is stored to the container, it is upcasted to the “Object” type. Polymorphism will recover the real object type when the object is retrieved. The container can store mixed types of “Object”s. The size of a reference does not change, but the type of the objects that the references point to can vary (from one child class to another). This is one of the reasons that Java automatically makes all classes inherits from the Object class directly or indirectly and “Class name;” declares a reference not an object.

Object-based Hierarchy

Page 10: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Not efficient in space usage: you have to create an IntObject object just to store an int (4 bytes). The corresponding class in Java is Integer, which inherits many methods (ToString(), GetClass(), …) from the Object class. Not efficient in time: all objects are dynamically created as heap objects. Extra work to create and dereference at runtime. Not efficient in coding: to use the container, programmers have to modify their existing data types to subclass from the Object class.

Object-based Hierarchy

Page 11: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

The template feature in C++ (TIC++V1:C16) and the generic feature in C# 2.0 (http://msdn.microsoft.com/en-us/library/512aeb7t(VS.80).aspx) provide a way to reuse source code, meaning we only have to write the same source code once for all different types.

The benefit of this type of solution compared to the object-based method are:1. Efficient in space usage: you don’t have to create an

IntObject object to store an int (4 bytes).2. Efficient in time: all objects can be created at compile

time as stack objects. No extra work to create and dereference at runtime.

3. Efficient in coding: to use template containers, programmers don’t need to modify their existing data types.

Templates

Page 12: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Declaration:template<class T>

T is a substitution parameter and represents a type name. T will be used in a class or method where you would normally see the

specific type names.

template<class T> class MyTemplate { T x; };

MyTemplate<int> y; // y is an object of MyTemplate with y.x instantiated as an int.

Template Syntax

Page 13: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

How does it work? The compiler makes the substitution of T to a real type name The process is also called instantiation of the template. It is done as part of the compilation process but before the

regular compilation. This is so called 2-pass compilation: instantiation and compilation.

The instantiation is done after preprocessing and is different from preprocessing.

The instantiation of a template is the replacement of the type parameter with a real type name (no memory allocation of any kind).

The instantiation of a class allocates the memory for an object of the class.

Templates

Page 14: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

#ifndef STACKTEMPLATE_H#define STACKTEMPLATE_H

template<class T>class StackTemplate { enum { size = 100 }; // another way of making size known at

compile time for an array T stack[size]; int top;public: StackTemplate() : top(0) {} void push(const T& i) {stack[top++] = i;} T pop() {return stack[--top];} int size() { return top; }};

#endif // STACKTEMPLATE_H

Template Stack

Example: StackTemplate.h

Page 15: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

#include "StackTemplate.h"#include <iostream>#include <fstream>#include <string>using namespace std;int main() { // use as an int stack StackTemplate<int> is; for(int i = 0; i < 8; i++) is.push(i); for(int k = 0; k < 8; k++) cout << is.pop() << endl; ifstream in("StackTemplate.cpp"); string line; // use as a string stack StackTemplate<string> strings; while(getline(in, line)) strings.push(line); while(strings.size() > 0) cout << strings.pop() << endl;}

Template Stack

Example: StackTemplate.h

Page 16: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

A template is basically a type parameter. The concept of type parameters makes it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. By using a type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or type conversions. The template type is substituted/instantiated at compile time, therefore no runtime overhead. One parameter can only represent one type, therefore a template stack can only hold one type of objects for each instantiation. An instantiated stack can’t hold objects of different types. The stack example here stores the values not the pointers of the objects. You may ended with duplicated objects if you keep copies of the objects outside the stack. The logic can be applied to develop one that stored pointers to objects.

Thinking in Templates

Page 17: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• Template arguments are not restricted to class types• You can also use built-in types. • The values of these arguments then become compile-time constants for that particular instantiation of the template. • You can even use default values for these arguments.

template<class T, int size = 100>class StackTemplate { T stack[size]; int top;public: StackTemplate() : top(0) {} void push(const T& i) {stack[top++] = i;} T pop() {return stack[--top];} int size() { return top; }};

Constants in Templates

Example: StackTemplate2.h

Page 18: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

#include "StackTemplate2.h"#include <iostream>#include <fstream>#include <string>using namespace std;

int main() { StackTemplate<int, 8> is; for(int i = 0; i < 8; i++) is.push(i); for(int k = 0; k < 8; k++) cout << is.pop() << endl; ifstream in("StackTemplate.cpp"); string line; StackTemplate<string> strings; while(getline(in, line)) strings.push(line); while(strings.size() > 0) cout << strings.pop() << endl;}

Constants in Templates

Example: StackTemplate2.cpp

Page 19: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Defining a template function:

template<class T> T max (T a, T b) { return (a > b ? a : b) ; }

Use of a template function:

cout << max(2,3);cout << max(2.3,3.4);

Template Function

Page 20: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Defining a template of more than one type parameters:

template<class T, class U> T max (T a, U b) { return (a > b ? a : b) ; }

Use of a template of more than one type parameters:

cout << max(8.8, 2);cout << max(2, 8.8); // anything wrong here?

Templates of more than one type parameters

Page 21: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Things need to known

Page 22: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• User-defined type consisting of a set of named integer constant (enumerators) Declararion:

enum enum-type-name { enum-list } enum-variable; enum enum-type-name { enum-list }; // enum-type-name is optional.

• Usage:// declaration of a variable of the type enum-type-name enum-type-name x; // assigning a value to the variable x = a member of the enum-list;

Enum

Page 23: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

#include <iostream>using namespace std;

int main() {enum Days {Sunday, Monday, Tuesday, Wednesday, Thursday,

Friday, Saturday};Days d;d = Sunday; cout << "Sunday: " << d << endl;d = Monday; cout << "Monday: " << d << endl;

enum Days1 {Sunday1 = 1, Monday1, Tuesday1, Wednesday1, Thursday1, Friday1, Saturday1};

Days1 d1;d1 = Sunday1; cout << "Sunday1: " << d1 << endl;d1 = Monday1; cout << "Monday1: " << d1 << endl;

enum Days2 {Sunday2 = 10, Monday2 = 20, Tuesday2, Wednesday2, Thursday2, Friday2, Saturday2};

Days2 d2;d2= Sunday2; cout << "Sunday2: " << d2<< endl;d2 = Tuesday2; cout << "Tuesday2: " << d2 << endl;

Enum

Example: enum.cpp

Page 24: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

// d2++; // will not compile, operator not defined// d2 = Thursday1; // will not compile, can't convert Days1 to

Days2// d2 = 10; // will not compile, can't convert int to Days2

int i = Monday2; cout << "i = " << i << endl;i = Monday;cout << "i = " << i << endl;i ++;cout << "i = " << i << endl;

}

Enum

Page 25: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• Compiler make sure only a value from the list is assigned to a enum variable. The variable is always in “range”.• The constants can be assigned to any int variables. But the compiler will not be able to make the check and the int variable may be manipulated to have an value out of the “range” of the list.• enum takes no space and is resoved at compile time. More efficient than static int.

class IntStack { enum { size = 100 }; int stack[size]; // …}

class IntStack { static const int size = 100; int stack[size]; //…}

Enum

Page 26: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Operators:

• An operator is a function.• operatorOperatorName is the name of the function internally.

e.g. operator++ is the name of the function internally represents ++.

• The operator (e.g. ++) can only be operated on objects of the class where the operator is defined.“this” pointer:

• “this” pointer is defined internally.• Passed as the first argument (hidden) of all functions of a class.• “this” pointer points to the address of the current object.

Page 27: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• “friend” is a reserved key word in C++ to give full access of any member of a class (private or not) to someone else (a class or a function).

class A {private: static string Hello () { return "Hi, there!"; }friend class B; // or friend B};

class B {public: string SayHi () { return A::Hello(); }

};

int main() {B b;

cout << b.SayHi()<< endl; }

Friend

Page 28: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• Any type you are using should be declared before being used (for the compiler to know what it is.)• Class A refers to B and Class B refers to A. Which one should be declared first?• Use forward declaration or class name definition (not class definition).

class B;class A {

private: static string Hello () { return "Hi, there!"; }friend B; // or friend class B};

class B {public: string SayHi () { return A::Hello(); }

};

int main() {B b;

cout << b.SayHi()<< endl; }

Forward Declaration

Page 29: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• Nested classes are classes inside a class. class A {

int x;public: A () { x = 7; cout << " x initialized.\

n"; }int getX() { return x; }

class B {int y;public: B() { y=8; cout << " y initialized.\

n"; }int getY() {return y;}B* getb() {return this;}

}b; // end B}; // end A

int main() { A o;A::B *bp = o.b.getb();cout << "o.b.getY() = " << o.b.getY() << endl;cout << "bp->getY() = " << bp->getY() << endl;

}

Nested Class

Page 30: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• The memory layout of o:• b is part of o.• bp stores the address of b and hence points to b.• No special access privileges to member functions of the nested class or the enclosing class. • But some compilers (g++ and VC++) treat the nested class as a full member of the enclosing class, therefore give the member functions in the nested class full access to the members of the enclosing class (including the private ones).

Nested Class

0xfff880xfff8888

o xx

A()A()

getX ()getX ()

yy

B()B()

getY()getY()

getb()getb()

b

bp

Page 31: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• The list is initialized before any statements of the constructor is processed. • A primitive/built-in type can be initialized as if it has an constructor.

class IntStackIter { IntStack& s; // private int index; // privatepublic: IntStackIter(IntStack& is) : s(is), index(0) { /* other statements */ }

// s = is, index = 0; // …};

Constructor Initializer List

Page 32: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

Iterators

Page 33: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• An iterator is an object that moves through a container of other objects and selects them one at a time, without providing direct access to the implementation of that container. • Iterators provide a standard way to access elements, whether or not a container provides a way to access the elements directly. • Iterators are used most often in association with container classes, and iterators are a fundamental concept in the design and use of the Standard C++ containers.• Iterator is a design pattern that promotes code reuse by accessing containers in a unified manner.

Iterators

Page 34: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

#include <iostream>using namespace std;

class IntStack { enum { ssize = 100 }; int stack[ssize]; int top;public: IntStack() : top(0) {} void push(int i) { stack[top++] = i; } int pop() { return stack[--top]; } friend class IntStackIter;};

Iterators

Example: iterator.cpp

Page 35: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

// An iterator is like a "smart" pointer:class IntStackIter { IntStack& s; int index;public: IntStackIter(IntStack& is) : s(is), index(0) {} int operator++() { // Prefix return s.stack[++index]; } int operator++(int) { // Postfix return s.stack[index++]; }};

Iterators

Example: iterator.cpp

Page 36: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

int main() { IntStack is; for(int i = 0; i < 8; i++) is.push(i); // Traverse with an iterator so that the code is reusable. IntStackIter it(is); for(int j = 0; j < 8; j++) cout << it++ << endl;}

Iterators

Example: iterator.cpp

Page 37: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• To aid in making things more generic, it would be nice to be able to say “every container has an associated class called iterator,” but this will typically cause naming problems.

• The solution is to add a nested iterator class to each container (notice that in this case, “iterator” begins with a lowercase letter so that it conforms to the style of the Standard C++ Library).

• We also need to make the stack generic instead of just for int.

Iterator Improvements

Page 38: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

// Simple stack template with nested iterator #ifndef ITERSTACKTEMPLATE_H #define ITERSTACKTEMPLATE_H #include <iostream>

template<class T, int ssize = 100> class StackTemplate { T stack[ssize]; int top; public: StackTemplate() : top(0) {} void push(const T& i) { stack[top++] = i; } T pop() { return stack[--top]; }

Iterator Improvements

IterStackTemplate.h

Page 39: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

class iterator; // Declaration required friend class iterator; // Make it a friend class iterator { // Now define it StackTemplate& s; int index; public: iterator(StackTemplate& st): s(st),index(0){} // To create the "end sentinel" iterator: iterator(StackTemplate& st, bool) : s(st), index(s.top) {} T operator*() const { return s.stack[index];} T operator++() { // Prefix form return s.stack[++index]; } T operator++(int) { // Postfix form return s.stack[index++]; }

Iterator Improvements

IterStackTemplate.h

Page 40: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

// Jump an iterator forward iterator& operator+=(int amount) { index += amount; return *this; } // To see if you're at the end: bool operator==(const iterator& rv) const { return index == rv.index; } bool operator!=(const iterator& rv) const { return index != rv.index; } friend std::ostream& operator<<( std::ostream& os, const iterator& it) { return os << *it; } }; iterator begin() { return iterator(*this); } // Create the "end sentinel": iterator end() { return iterator(*this, true);} }; #endif // ITERSTACKTEMPLATE_H ///:~

Iterator Improvements

IterStackTemplate.h

Page 41: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

//: C16:IterStackTemplateTest.cpp #include "IterStackTemplate.h" #include <iostream> #include <fstream> #include <string> using namespace std;

int main() { StackTemplate<int> is; for(int i = 0; i < 20; i++) is.push(i); // Traverse with an iterator: cout << "Traverse the whole StackTemplate\n"; StackTemplate<int>::iterator it = is.begin(); while(it != is.end()) cout << it++ << endl;

Iterator Improvements

IterStackTemplateTest.cpp

Page 42: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

cout << "Traverse a portion\n"; StackTemplate<int>::iterator start = is.begin(), end = is.begin(); start += 5, end += 15; cout << "start = " << start << endl; cout << "end = " << end << endl; while(start != end) cout << start++ << endl; ifstream in("IterStackTemplateTest.cpp"); string line; StackTemplate<string> strings; while(getline(in, line)) strings.push(line); StackTemplate<string>::iterator sb = strings.begin(), se = strings.end(); while(sb != se) cout << sb++ << endl; } ///:~

Iterator Improvements

IterStackTemplateTest.cpp

Page 43: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• The * operator selects the current element, which makes the iterator look more like a pointer and is a common practice. • You can “jump” an iterator forward by an arbitrary number of elements using operator+=. • Two overloaded operators: == and != compare one iterator with another. They are primarily intended as a test to see if the iterator is at the end of a sequence in the same way that the “real” Standard C++ Library iterators do. The idea is that two iterators define a range, including the first element pointed to by the first iterator and up to but not including the last element pointed to by the second iterator. • Note that the end iterator, which we often refer to as the end sentinel, is not dereferenced and is there only to tell you that you’re at the end of the sequence. Thus it represents “one past the end.”

Iterator Improvements

Page 44: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

• The container member function begin( ) uses the first iterator constructor that defaults to pointing at the beginning of the container (this is the first element pushed on the stack). • However, a second constructor, used by end( ), is necessary to create the end sentinel iterator. Being “at the end” means pointing to the top of the stack, because top always indicates the next available – but unused – space on the stack. This iterator constructor takes a second argument of type bool, which is a dummy to distinguish the two constructors.

Iterator Improvements

Page 45: Templates Containers Iterators (TIC++V1:C16) Yingcai Xiao 06/26/2008

What? How? Why? Inside?

• Template• Container• Iterator• object code reuse• source code reuse• object-based hierarchy• enum• operator• friend• nested class

Summary