templates “generic programming” ece 297. 2 templates a way to write code once that works for...
TRANSCRIPT
Templates
“Generic Programming”
ECE 297
2
Templates
• A way to write code once that works for many different types of variables– float, int, char, string, List, Vector, …
• Reference: Problem Solving in C++ by Savitch, Chapter 17
Template Functions
4
Types & Functions
• All parameters and local variables in a function must have a type
void swap_val (int &a, int &b){ int temp; temp = a; a = b; b = temp;}
void swap_val (double &a, double &b){ double temp; temp = a; a = b; b = temp;}
OK.Function overloading
5
Function Templates
• Allow you to write “abstract algorithms”– Work for many types
template<class VariableType>void swap_val (VariableType &a, VariableType &b){ VariableType temp; temp = a; a = b; b = temp;}
“template prefix”defines a type parameter
Can use that “type parameter”instead of a regular typeIn the function
6
Using a Function Templatetemplate<class VariableType>void swap_val (VariableType &a, VariableType &b){ VariableType temp; temp = a; a = b; b = temp;}
int main () { int i1 = 5, i2 = 10; swap_val (i1, i2); // Compiler will create swap_val<int>
float f1 = 1e6, f2 = 2e6; swap_val (f1, f2); // Compiler will create swap_val<float>}
Template definition must come before template use
7
Using a Function Template
#include “swap_val.h”int main () { int i1 = 5, i2 = 10; swap_val (i1, i2); // swap_val<int> created
float f1 = 1e6, f2 = 2e6; swap_val (f1, f2); // swap_val<float> created}
template<class VariableType>void swap_val (VariableType &a, VariableType &b){ VariableType temp; temp = a; a = b; b = temp;}
main.cpp
swap_val.h
8
Restrictions on Swap
swap_val (int, int); swap_val (char, char);swap_val (List, List);swap_val (int, float);
Works for any one type that has proper (deep copy) operator=
template<class VariableType>void swap_val (VariableType &a, VariableType &b){ VariableType temp; temp = a; a = b; b = temp;}
9
Generic Minimumtemplate<class VariableType>VariableType minimum (VariableType a, VariableType b){ if (a < b) return (a); else return (b);}
minimum (int, int);minimum (float, float); minimum (int[20], int[20]);minimum (List, List);minimum (string, string);
• Works for any type that defines operator< (reasonably)
Template Classes
14
Template Classes
• Can make templated (type-generic) classes– Member functions– Member data
• Most useful for container classes– Classes that implement basic data structures– List, Vector, Tree (Map), …
15
Example: Pair of Data
• Often useful to store two pieces of data together– key, value in ECE 244 lab5 (binary tree for DNS) used together– string name, int ip (244 lab 5)– Could want: string name, string data– or int key, float data– …
• Make a class that can store two arbitrary pieces of data– first (any type)– second (any type)
16
Generic Pair Classtemplate<class Tfirst, class Tsecond>class MyPair {public: MyPair (Tfirst _first, Tsecond _second); void setFirst (Tfirst _first); void setSecond (Tsecond _second); Tfirst getFirst (); Tsecond getSecond ();
private: Tfirst first; Tsecond second;};
In MyPair.h
17
Generic Pair Class#include “MyPair.h”
int main () { MyPair<int,float> pair1(1, 2.5); cout << pair1.getFirst() << “ “ << pair1.getSecond(); ...
int first = 1float second = 2.5
pair1
In main.cpp
Creates a variable named pair1class (type) is MyPair<int, float>
18
Generic Pair Class#include “MyPair.h”int main () { MyPair<int,float> pair1(1, 2.5); cout << pair1.getFirst() << “ “ << pair1.getSecond();
MyPair<string,double> tscore ("Your test score", 7.5); cout << tscore.getFirst() << " " << tscore.getSecond() << endl; tscore.setFirst ("Revised test score"); tscore.setSecond (9.5); cout << tscore.getFirst() << " " << tscore.getSecond() << endl;}
string first = “Your test score”double second = 7.5
tscore
In main.cpp
Creates a variable named tscoreclass (type) is MyPair<string, double>
19
Generic Pair Class#include “MyPair.h”int main () { MyPair<int,float> pair1(1, 2.5); cout << pair1.getFirst() << “ “ << pair1.getSecond();
MyPair<string,double> tscore ("Your test score", 7.5); cout << tscore.getFirst() << " " << tscore.getSecond() << endl; tscore.setFirst ("Revised test score"); tscore.setSecond (9.5); cout << tscore.getFirst() << " " << tscore.getSecond() << endl;}
1 2.5Your test score 7.5Revised test score 9.5
Program output
In main.cpp
22
Writing Templates
• Write code for a specific type first– Debug, test
• Convert to template– Can get complex / subtle syntax errors
STL: Generic Algorithms
Using the Standard Template Libraries
24
STL
• Writing templates fairly hard• Using templates easy & productive• Standard Template Library
– Fast, well-tested implementations of useful algorithms and data structures
25
STL References
Basic Reference: “Problem Solving in C++” by Savitch, Chapter 18
Detailed reference: cplusplus.com
Really nitty gritty details: “C++ Primer” by Lippman, Lajoie and Moo
26
STL
• Writing templates fairly hard• Using templates easy & productive• Standard Template Library
– Fast, well-tested implementations of useful algorithms and data structures
27
STL: Generic Algorithms
#include <algorithm>using namespace std;
int main ( ) { double x = 1.1, y = 1.2; double z = max (x, y); int j = 2, k = 3; int i = max (j, k); z = max (i, x); z = max (x, 2); z = max (x, 2.0); }
main.cpp
// Won’t compile// Won’t compile
// OK! max (double, double)
Useful algorithms that work with many types in <algorithm>template<class T>T max (T a, T b) { if (a < b) return (b); else return (a);}
<algorithm>
// OK max (int, int)
28
STL: Generic Algorithms
Also in <algorithm>: min (), sort ()• Work for any one type that defines < and assignment
(operator=)
and lots more – see cplusplus.com• But I only use the very basic ones
STL Container Classes
30
STL: Container Classes
• Most useful part of STL – Data structures that just “contain” some other data
type / class– E.g. vector, linked list, binary tree of some type
• Can use to store any type of data (templated)– Avoids a lot of repetitive coding of linked lists, binary
trees, etc.
31
Vector• Example motivation:
– Want to read integers from cin to an array until EOF> 1 -20 3 31 55 <Ctrl+D>
– Then pass the array on to the rest of the program to process, print, examine, …
– Problem: how big an array should we allocate?int *array = new int[??];
– Don’t know until after the input is read!– Could code up a linked list, load it, count elements,
then allocate array, copy data from linked list, delete linked list
32
Vector
• Wouldn’t it be great to have something just like an array that could grow as needed?
#include <vector>#include <iostream>using namespace std;
vector<int> get_input ( ) { vector<int> vec; // vector of ints, initially empty int val; cin >> val; while ( !cin.fail() ) { vec.push_back (val); // Add new value to end of vector cin >> val; } return (vec); // Return the whole vector}
main.cpp
33
Vector: Can Use Like Array#include <vector>#include <iostream>using namespace std;
. . .
int main () { vector<int> in_vals; in_vals = get_input (); for (int i = 0; i < in_vals.size(); i++) cout << in_vals[i] << “ “;
cout << endl;
for (int i = in_vals.size() – 1; i >= 0; i--) cout << in_vals[i] << “ “;}
main.cpp
vectors know how many elements they contain. Valid data from index 0 to size()-1
Fast, O(1), random access to any entry
How would I print out the vector in reverse order?
Input: 1 -20 3 31 55 <Ctrl+D>Output: 1 -20 3 31 55 55 31 3 -20 1
34
Slightly Cleaner Version
int main () { vector<int> in_vals; in_vals = get_input (); for (int i = 0; i < in_vals.size(); i++) cout << in_vals[i] << “ “;}
Compiler will give a type mismatch warning (.size() is unsigned int). Harmless, but I prefer to have no
warnings.
for (int i = 0; i < (int) in_vals.size(); i++) cout << in_vals[i] << “ “;
Will make warning go away
for (vector<int>::size_type i = 0; i < in_vals.size(); i++) cout << in_vals[i] << “ “;
C++ purist’s way i now unsigned int watch out for >= 0 tests
35
How Does It Work?
Input:
vector<int> get_input ( ) { vector<int> vec; // vector of ints, initially empty int val; cin >> val; while ( !cin.fail() ) { vec.push_back (val); // Add new value to end of vector cin >> val; } return (vec); // Return the whole vector}
int *arrayint sizeint capacity
1
1 -20
2
1 -20
3 311 -20
1 -20 3 31 55
481123450
3 31 55
vec
36
Vector: Key Properties
• Efficient to add elements at end (push_back)– Because when there isn’t enough space it
grows the storage quite a bit (usually 2x)– Means even for a large number N of
push_back(), we get only a few array copies– O(1) on average
• Efficient random access to data– Because internally the vector stores the data
in an array operator[ ] can be fast– O(1)
37
Handy Constructors
#include <vector>using namespace std;
int main () { vector<int> vec1; // default constructor: size = 0 vector<int> vec2(10); // size = 10, but values unknown vector<int> vec3(10,-1); // size = 10, all values -1}
38
Assignment & Destruction
#include <vector>using namespace std;
int main () { int *a = new int[10]; int *b = new int[10]; vector<int> v1(10), v2(10); a[0] = 2; v1[0] = 2; . . . a = b; // OK? v1 = v2; // OK? delete[] a; delete[] b; delete v1; // Should I? }
No! Shallow copyYes! Proper deep copy defined in <vector>
No! <vector> defines a destructor; cleans up its internal array
39
operator[]
#include <vector>using namespace std;
int main () { vector<int> v1; v1.push_back(-3);
v1[0] = -2; // OK? v1[1] = -2; // OK? v1.push_back(-3); v1[1] = -2; // OK?}
Yes, entry 0 existsNo, can’t create values with [ ]Yes, can create values with push_back()Yes, entry 1 exists now
Really bad out of bounds array access and memory corruption (maybe seg fault, maybe worse!)• Most compilers have an option to make vector check the [ ] index value is
between 0 and size()-1 and print an error (g++ -D_GLIBCXX_DEBUG)• Slows program down we have turned this on only for the debug
configuration of your milestone1 NetBeans project
40
Shrinking a vector
#include <vector>using namespace std;
int main () { vector<int> v1; v1.push_back(1); v1.push_back(2);
for (int i = 0; i < in_vals.size(); i++) cout << in_vals[i] << “ “;
v1.pop_back(); // Removes one entry from end of vector for (int i = 0; i < in_vals.size(); i++) cout << in_vals[i] << “ “;}
Output: 1 2
Output: 1
41
Vectors should be familiar
• vector<char>• almost same as <string>
– Difference: string defines some extra utility functions– Otherwise a string really is a vector<char>
• Full reference on vector member functions at http://www.cplusplus.com/reference/vector/vector/