230 likes | 380 Views
Templates. Class Stack Generic Stack Potential Problems Class string Class Pear. class Stack{ public: Stack(){ data = new int[10]; top=-1;} ~Stack(){ delete [] data;} Stack& push(int e){ data[++top]=e;return *this;} Stack& pop(int &e){
E N D
Templates • Class Stack • Generic Stack • Potential Problems • Class string • Class Pear
class Stack{ public: Stack(){ data = new int[10]; top=-1;} ~Stack(){ delete [] data;} Stack& push(int e){ data[++top]=e;return *this;} Stack& pop(int &e){ e = data[top--];return *this;} void print() const{ for(int i=top;i>=0; i--) cout<<data[i]<<' '; } private: int top int *data; }; Class Stack void main(){ Stack s; s.push(1).push(2); s.push(3).push(4); s.print(); //4 3 2 1 int x = 0; s.pop(x); cout<<"x = "<<x; //x = 4 } *(See stack.cpp.txt)
Problems with This Class • Some can be fixed • Size of stack restricted to 10 (maximum) • No checks for pushing full stack or popping empty stack • Some cannot be fixed • What if we want to add a stack of real numbers (double or float)? • What if we want to have a stack of character strings?
Solution • Do not want to write a new version of Stack of each potential data type • int, float, double, char, string • Instead, we need to identify a generic framework for a Stack that we can then use to build • Stack of integers • Stack of characters • Stack of doubles
Templates • Class templates also called parameterized types • Require one or more parameters to specify the generic class type • Need to make slight changes to syntax in • Class declaration • Implementation of the data structure • Example: see GenStack.cpp.txt
Changes in Class Declaration template <class S> class GenStack{ public: GenStack(){ data = new S[10]; top=-1;} ~GenStack(){ delete [] data;} GenStack& push(S); GenStack& pop(S &); void print() const; private: int top; S *data; };
Changes in Member Functions template <class S> GenStack<S>& GenStack<S>::push(S e){ data[++top] = e; return *this;} template <class S> GenStack<S>& GenStack<S>::pop(S &e){ e = data[top--]; return *this;} template <class S> void GenStack<S>::print() const{ cout<<'['; for(int i=top; i>=0; i--) cout<<data[i]<<' '; cout<<']'<<endl; }
Changes for Class Users void main(){ GenStack<int> s1; s1.push(1).push(2).push(3).print(); //[3 2 1 ] GenStack<double> s2; s2.push(5.5).push(6.6).push(7.7).print(); //[7.7 6.6 5.5 ] GenStack<char> s3; s3.push('a').push('b').push('c').print(); //[c b a ] }
typename • You can use either “<class T>” or “<typename T>” when using templates
Still Have Problems • Works for primitive data types, but not for non-primitive data types, unless • The non-primitive data type has a constructor that takes no arguments • The non-primitive data type supports the assignment (=) operator • The non-primitive data type supports the copy constructor
Example Problem int main(){ GenStack<char *> s; char *str1 = new char[10]; strcpy(str1,"string1"); char *str2 = new char[10]; strcpy(str2,"string2"); char *str3 = new char[10]; strcpy(str3,"string3"); s.push(str1).push(str2).push(str3); s.print(); //[string3 string2 string1 ] delete str1; char *str4 = new char[10]; strcpy(str4,"ABCxyz"); s.print(); //[string3 string2 ABCxyz ] return 0; } //See problem.cpp
GenStack<char*> Problems • Push: data[++top] = e; • Pop: e = data[top--]; • This is not how you deal with “char*” data • Use strcpy instead • Must allocate space to store copy of string • Data type S must have the operations defined for it that you use with the class • The assignment operator for strings is not "="
Solution • Use Class string • C++ provides a pre-defined class called “string” • Has a constructor with no arguments • Has an assignment operator • Has a copy constructor • So we can use this in our program instead of the C-type character arrays
String Solution #include <string> using std::string; int main(){ GenStack<string> s; { string *str1 = new string("string1"); string str2("string2"); string str3; str3 = "string3"; s.push(*str1).push(str2).push(str3); s.print(); //[string3 string2 string1 ] delete str1; string *str4 = new string("string4"); } s.print(); //[string3 string2 string1 ] return 0; } //See classString.cpp
Works for Class Pear void main(){ GenStack<Pear> s; Pear *p = new Pear; s.push(*p); p = new Pear(2); s.push(*p); p = new Pear(3,3); s.push(*p); s.print(); //[(3,3) (2,0) (0,0) ] delete p; s.print(); //[(3,3) (2,0) (0,0) ] }
Class Pear Refresher class Pear { int a,b; public: /*constructor with no arguments*/ Pear(){ a=0; b=0; } Pear(int x){a=x; b=0;} Pear(int x, int y){a=x; b=y;} /*copy constructor*/ Pear(const Pear &p){ a=p.a; b=p.b; }
Class Pear Refresher /*assignment operator*/ const Pear& operator=(const Pear &p){ a=p.a; b=p.b; return *this; } void setA(int x){a=x;} void setB(int x){b=x;} int getA(){return a;} int getB(){return b;} friend ostream & operator<<(ostream & banana, const Pear & p){ banana<<'('<<p.a<<','<<p.b<<')'; return banana;} };
Why Class Pear Works • Constructor with no arguments: GenStack(){ data = new S[10]; top=-1; } • The GenStack constructor makes an array of 10 objects calling a constructor which has no arguments • Class Pear has a constructor with no arguments
Why Class Pear Works • Assignment operator: • Push: data[++top] = e; • Pop: e = data[top--] • Since a memberwise assignment operator is defined automatically, this works for classes with non-dynamic data types (data members that are not pointers) • For dynamic data types, such as a binary tree, the appropriate assignment operator (& copy constructor) must be created by the programmer
Why Class Pear Works • Copy Constructor • GenStack<S>& GenStack<S>::push(S e) • In this function, the copy constructor is called (pass by value) • GenStack<S>& GenStack<S>::pop(S &e) • In this function, the copy constructor is not called (pass by reference)
Essential Member Functions • Constructor with no arguments Pear(){a=0; b=0;} • Assignment operator Pear(const Pear &p){ a=p.a; b=p.b;} • Copy constructor const Pear& operator=(const Pear &p){ a=p.a; b=p.b; return *this;} (See pear.cpp)
Class Exercise 1 • What’s the output of this program? • exercise1.txt