290 likes | 352 Views
Function O verloading , T emplated F unctions , Templated C lasses , Pointers to Functions. Horton pp. 247 – 254 (function overloading and templated functions) Tapestry Sect. 11.2 (for templated functions)
E N D
FunctionOverloading, TemplatedFunctions, TemplatedClasses, Pointers to Functions Horton pp. 247 – 254 (function overloading and templated functions)Tapestry Sect. 11.2 (for templated functions) Horton pp. 378 – 412 (templated classes with some advanced issues that we will not see)Tapestry Section12.3.6(templated classes – more condensed) Horton pp. 231 – 237 (Pointers to Functions) Functors (Function Objects) are at the end of these slides, but we will skip them (not responsible)
Aim Sometimes you need variations of the same basic function: • one to sort an array of integers and one to sort an array of characters • one to swap two variables of type int, one to swap two variables of type char • one to find the maximum element of an array of ints, one for array of strings • . . . You can: • define two different functions taking different types of arguments (function overloading) 2. use a templated functionwhich uses a templated type
Function overloading Define twodifferentfunctionsthattaketheappropriateparametertypes: voidSort (vector<int> & a) voidSort (int a[], int size) {...} {...} voidSort (vector<string> & a) voidSort (string a[], int size) {...} {...}
Functionoverloading Define: voidSort (vector<int> & a) {...} voidSort (vector<string> & a) {...} Theappropriatefunctionwill be calleddepending on theparameter. E.g. vector <int> nums; vector <string> names; sort(nums); //callsthefirstfunction sort(names); //callsthesecondfunction Thismay be therightsolution in somecases. But functionoverloadingduplicatescode: • maintaining is difficult (2-3 versions of a sortfunction); ifthere is a change, youhavetoupdateall
Function overloading Also, you cannot do this with function overloading: int FindRoots(double a, double b, double c) double FindRoots(double a, double b, double c) Why? • Because from the types of the parameters passed, you cannot determine which function should be called • The signatures (parameters+function name) of two different functions should not be completely the same • You cannot differentiate between two functions just using their return type
template <class Type> void Swap(Type &a, Type &b) { Type temp; temp = a; a = b; b = temp; } Any name can be the template parameter (e.g. Type, myType, sablonTipi, ...) simple form for ints: void Swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; } Templatedfunctionswriting a singleinstancefor a functionwithgenerictype name
template <class Type> void Swap(Type &a, Type &b) { Type temp; temp = a; a = b; b = temp; } int main() { double d1, d2; cin >> d1 >> d2; Swap (d1, d2); string s1, s2; cin >> s1 >> s2; Swap (s1, s2); return 0; } void Swap(double&a, double&b) { double temp; temp = a; a = b; b = temp; } Templated functions Not explicitly written but compiled in this way void Swap(string&a, string&b) { string temp; temp = a; a = b; b = temp; }
Templatedfunctions: morecomplex How can we swap the values at the given locations of a vector? template <class T>//note: changed Type to T void Swap(vector<T> & list, int i, int j) { T temp; temp = list[i]; list[i] = list[j]; list[j] = temp; } You can have a local variable of a templated type, but you should have at least one parameter of the same template type. In short, everywhere you had a element type (e.g. int), you put a new templated type name (e.g. T), and add template <class typename>at the beginning of the function.
Templatedfunctions: morecomplex template <class Type> void Print(const vector<Type> & list) { int i, count; count = list.Size(); for (i=0; i < count; i++){ cout << list[i] << endl; } }
Templatedfunctions: moreusesandrules template <class T> T DoStuff(T myvar); //template types can be return type, but there must be at least //one parameter of template type template <class T> TdoThis (int x); //this is not valid template <class noName> noName FindLastElement(vector<noName> & list1); //this is valid template <class T1, class T2> intCompareLists(vector<T1> & list1, vector<T2> & list2); //note that we have two types (T1 and T2) // in general you can have as many types as you need variable types in your function
functionoverloadvstemplatedfunctions: comparison(Tapestrypp 543) Function overloading duplicates code: • maintaining is difficult (multiple versions of a function that does the same job for different parameter types) • each must be changed if there is a need Function templates: • reuses code, rather than to duplicate it • causes code bloat: each instantiation of a function (i.e. each call to the function using a diff. type) adds new object code: that is why it is called template • Function Templates are like macros... You should also consider reusing simple functions with casting as a 3rd alternative, (when suitable) since in this case there is neither code duplication NOR code bloat! • Define: void Swap (double & a, double & b ) • Use with ints as: int a, b; Swap( (double)a, (double)b ); You should use this option when the task is suitable and –of course– types are easily convertable!
TemplatedClasses Similar to the idea of a templated function, you can have templated classes. Main idea: • types are not fixed at class declaration and definition; they are instantiated when the objects are created Similar to templated functions, these provide type flexibility in class definitions. For instance, the linked list class should be able to be used for integers or strings, ... • e.g. Tapestry's tvector and standart vector classes are templated classes vector<int> myintarr(1000); vector<string> mystrarr(1000);
TemplatedClasses It is a good idea to develop a non-template version first, and then convert to a templated class later. Such a conversion is mostly changing the syntax of declarations and function/class headings by applying some simple rules. We will convert our previously developed LinkedList class with integer container to a templated class. But before that I will give a smaller example Normally what you have to do are: • to add template <class itemType>before the class heading and before each member function implementation (not declaration). Here itemType is the element type that you store in a node. • to replace each of the occurrence of the element type with itemType everywhere in the class declaration (linkedlist.h file) and class implementation (linkedlist.cpp file). • to add <itemType>after the class name (in our case after LinkedList) in the class implementation file (linkedlist.cpp) – There is an exception for this rule for the constructors/destructor. See the example codes in the coming slides.
TemplatedClasses This procedure works fine if you do not use another templated class or templated struct in your class. If you define your struct within the class (as in the case of LinkStringSet class), again this procedure works. However, if you declare another templated class or a templated struct (in our case struct node) before your class, but want to use them in your class, you have to refer to these other structs/classes by adding <itemType>after the struct/class names everywhere (in both .h and .cpp files of your class). • This is the case in our LinkedList example Structs can also be templated like classes (the rules are the same)
TemplatedClasses – Small Example template <class OpType> class Add { public: Add (OpType op1, OpType op2); OpType operand (int i); OpType result (); private: OpType myOp1, myOp2; }; template <class OpType> Add<OpType>::Add (OpType a, OpType b) :myOp1(a), myOp2(b) {} template <class OpType> OpType Add<OpType>::operand (int i) {if (i==1)return myOp1; else if (i==2)return myOp2; } template <class OpType> OpType Add<OpType>::result () {return myOp1 + myOp2; } int main() { Add<int> a(5, 12); cout << a.operand(1) << " + " << a.operand(2) << " = " << a.result() << endl; Add<string> b("cs", "204"); cout << b.operand(1) << " + " << b.operand(2) << " = " << b.result() << endl; Add<char> c('2', '3'); cout << c.operand(1) << " + " << c.operand(2) << " = " << c.result() << endl; return 0; } A class for Addition. • Two private data members are added using + operator. Private data members are templates In main, class is instantiated using three different types for OpType: int, string, char. What is output? See addtemplate.cpp 5 + 12 = 17 cs + 204 = cs204 2 + 3 = e
TemplatedClasses – LinkedList class • First we need to convert the node struct to template template <class itemType> struct node { itemType info; node *next; node () { } node (const itemType & s, node * link) : info(s), next (link) { } };
TemplatedClasses – LinkedList class • Then the class definition is converted (LinkedListTemplate.h) template <class itemType> class LinkedList { private: node<itemType> * head; int size; public: LinkedList (); LinkedList (const LinkedList &); ~LinkedList (); void printList() const; void addToBeginning(itemType n); void deleteList (); const LinkedList & LinkedList::operator = (const LinkedList & rhs); node<itemType>*createClone () const; };
TemplatedClasses – LinkedList class • After that member functions are converted (LinkedListTemplate.cpp) • Some examples here (see LinkedListTemplate.cpp for all) //copy constructor template <class itemType> LinkedList<itemType>::LinkedList (const LinkedList<itemType> & copy) { head = copy.createClone(); size = copy.size; } //destructor template <class itemType> LinkedList<itemType>::~LinkedList () { node<itemType> * ptr = head; while (ptr != NULL) { node<itemType> * temp = ptr->next; delete ptr; ptr = temp; } } //constructor template <classitemType> LinkedList<itemType>::LinkedList () { head = NULL; size = 0; }
TemplatedClasses – LinkedList class template <class itemType> void LinkedList<itemType>::addToBeginning (itemType n) { node<itemType> *ptr = new node<itemType>(n,head); head = ptr; size++; } template <class itemType> const LinkedList<itemType> & LinkedList<itemType>::operator = (const LinkedList<itemType> & rhs) { if (this != &rhs) { deleteList(); head = rhs.createClone(); size = rhs.size; } return *this; }
TemplatedClasses – LinkedList class template <class itemType> node<itemType> * LinkedList<itemType>::createClone () const { if (head == NULL) return NULL; node<itemType> * headClone = new node<itemType> (head->info, NULL); node<itemType> * ptr = head->next; //second node in orig. node<itemType> * ptrClone = headClone; //to track the clone list while (ptr != NULL) { ptrClone->next = new node<itemType> (ptr->info, NULL); ptr = ptr->next; ptrClone = ptrClone->next; } return headClone; }
TemplatedClasses – LinkedList classUsing in main int main() { string baskan[] = {"Ibrahim", "Aziz", "Unal", "Fikret"}; LinkedList<string>strlist;//Linked list object with string element type LinkedList<int> intlist;//Linked list object with integer element type for (int k=0; k < 4; k++) { strlist.addToBeginning(baskan[k]); intlist.addToBeginning(k+1); } cout << "string list contains:\n"; strlist.printList(); cout << "integer list contains:\n"; intlist.printList(); } See linkedlisttemplate.h, linkedlisttemplate.cpp and linkedlisttemplatedemo.cpp
TemplatedClasses – LinkStringSet class See Tapestry pp. 625 – 631 for the templated version of LinkStringSet class.Tapestryrenamed templated version of thisclass as LinkSet Conversion to template is not different than what we have done for our LinkedList class, but in LinkSetclass the Nodestruct definition is in the class definition. Thus Nodestruct is not templated and referral to node does not require to add <itemType>totheend. However, youhavetorefertoNodeoutside of theclassdefinitionbyputtingtypenameLinkSet<itemType>:: beforeNode Theuse of typenamekeyword is not mentioned in Tapestry, but it is needed in Visual Studio 2012 (also in someearlierversions of VS).
Fundemental Dilemma in using TemplatedClasses (Tapestry pp.628) A templated class cannot be compiled without instantiating. The reason is simple, there is nothing to be compiled in the template itself! Even if you have both class declaration (.h) and class implementation in the same translation unit (which is the case in linkedlisttemplate.cpp since we include linkedlisttemplate.h at the beginning) compilation means nothing without knowing the corresponding real types for the templates. • Compilation succeeds but does not produce the expected object code for the class member functions • Thus when you use the class member functions in the main program, the linker will complain by some unresolved external symbol errors. • Let us generate this case in our templated Linked List class. Types corresponding to templates become apparent when we generate objects in the main program. E.g. LinkedList <string> strlist; • Thus, class declaration (linkedlisttemplate.h), member function implementations (linkedlisttemplate.cpp) and the user program (linkedlisttemplatedemo.cpp) must be in the same translation unit. • There are some ways of doing this (see next slide)
Solutions to Fundemental Dilemma Solution 1: Have both class declaration and implementation in the same file (name is not important but say .h file) and include it at the beginning of the user program file. Solution 2 (widely used): Keep separate .h (class declaration ) and .cpp (class implementation) files • But at the end of the class header file (linkedlisttemplate.h) put #include "linkedlisttemplate.cpp" • Include the header file (linkedlisttemplate.h) at the beginning of the user program, as usual. In this way, translation unit includes everything • Caution 1: Do not add class impl. file (linkedlisttemplate.cpp) to the project; this would cause lots of redefinition errors. • Caution 2: Since the header file is also included at the beginning of linkedlisttemplate.cpp (which actually is not needed in this approach), if you do not make the classical control to guard multiple header file inclusion you can have infinite inclusion. Thus you have to have the following structure in the header file: #ifndef _LINKEDLISTTEMPLATE_H #define _LINKEDLISTTEMPLATE_H // the class definition #endif • Let's use this approach in our case Solution 3: Keep separate .h (class declaration ) and .cpp (class implementation) files and include both at the beginning of the user program
FunctionPointers (Pointers to Functions) Consider the following scenario: You have 2 or more functions with the same prototype other than their names • i.e. they have same return types and same types for the parameters • Example: double f1 (int, int, double); double f2 (int, int, double); . . . • In another function (say myF), you want to call one of these functions, but you do not know which one while implementing myF • So you want to make the "function to call"a parameter of myF and pass the actual function to call as an argument while you call myF Can you pass a function as parameter to another function? Yes, using pointers to functions.
Pointers to Functions You can generate a pointer to a function using the following syntax Type(* PointerName)(Parameter list ) • Type must match the function's return type • Parameter list must match the function's parameter list • Don't forget parentheses before and after * PointerName When you want to assign a function to a function pointer, you get the address of function using & and assign it to the function pointer: PointerName= & FunctionName; When you want to call the function using function pointer use: (*PointerName)(Argument list) See an example in funcptr.cpp
END OF THIS PPT SKIP THE REST (actually the rest of this ppt file is not shown in slide show mode since the rest of the slides are hidden)