()); // prints out (shuffled) random_shuffle (numbers, numbers + array_dimension); return 0; } native iterator type (pointer) user-defined iterator type native container type (array) calls to STL algorithms user-defined functor constructor calls"> ()); // prints out (shuffled) random_shuffle (numbers, numbers + array_dimension); return 0; } native iterator type (pointer) user-defined iterator type native container type (array) calls to STL algorithms user-defined functor constructor calls">
Download presentation
Presentation is loading. Please wait.
1
Motivation for Generic Programming in C++
We’ve looked at procedural programming Reuse of code by packaging it into functions We’ve also looked at object-oriented programming Reuse of code and data by packaging them into classes However, these techniques alone have limitations Functions are still specific to the types they manipulate E.g., swap (int, int) and swap (int *, int *) do essentially same thing But, we must write two versions of swap to implement both Classes alone are still specific to the types they contain E.g., keep an array (not a vector) of dice and an array of players Must write similar data structures and code repeatedly E.g., adding a new element to an array Generic programming aims to relieve these limitations
2
C++ STL Generic Programming Example
#include <iostream> #include <iterator> #include <algorithm> using namespace std; int main (int, const char **) { int numbers [] = {0, 9, 2, 7, 4, 5, 6, 3, 8, 1}; size_t array_dimension = sizeof (numbers) / sizeof (int); // prints out (ascending sort) sort (numbers, numbers + array_dimension); copy (numbers, numbers + array_dimension, ostream_iterator<int>(cout, " ")); cout << endl; // prints out (descending sort) sort (numbers, numbers + array_dimension, greater<int>()); // prints out (shuffled) random_shuffle (numbers, numbers + array_dimension); return 0; } native iterator type (pointer) user-defined iterator type native container type (array) calls to STL algorithms user-defined functor constructor calls
3
Decoupling Data Types from Algorithms
STL has an architectural separation of concerns Containers hold data of a particular type Iterators give access to ranges of data Algorithms operate on particular kinds of iterators Functors plug in functions to modify algorithms Containers include arrays, vector, list, set Iterators include pointers, ostream_iterator Algorithms include sort, copy, random_shuffle Functors include less, greater
4
Decoupling Data Types from Algorithms (cont.)
Iterators decouple algorithms from containers Algorithms require specific iterator interfaces Containers provide iterators that give specific interfaces Allows safe combinations, disallows unsafe/inefficient ones E.g., algorithm can use ++ for iterator over linked list or array E.g., algorithm can only use [ ] or += for iterator over array (not list) Functors decouple algorithms from their sub-steps E.g., algorithms like sort use a comparison function Sorting is valid for a variety of different comparisons And may give different results for each of them Allowing different comparison functions to be plugged in Lets the sort default to producing an ascending order Lets a different functor be plugged in to produce a descending order
5
Supporting Native and User Defined Types
Native arrays provide a natural container abstraction Hold a (contiguous) range of elements Can access the values at different positions Can change the values at different positions With pointers, new, and delete, can even re-dimension them Native pointers provide a natural iterator abstraction Point to a position in a container (e.g., an array) Can move from one position to the next (e.g., ++, +=) Can be dereferenced to obtain the value at aliased location Since C++ STL can’t change how native types work It uses arrays and pointers as a container/iterator example It ensures that user-defined containers/iterators are similar Pointers support all operations any STL iterator must provide
6
Support for Generic Programming in C++
The power of C++ generics comes from 3 main ideas Decoupling data types from algorithms operating on them Seamlessly supporting both native and user defined types Templates: type parameterization, interface polymorphism Templates introduce type parameterization Allows you to write functions like swap once … … and then plug in parameter types as needed Allows you to write class templates … … and then plug in member variable types later This already allows a great deal of flexibility, e.g., list<int> intlist; list<string> stringlist; where list is a template, and int and string are used as type parameters to different instantiations of that template Templates introduce interface polymorphism Templates impose type requirements on their parameters Can plug in any types for which template’s syntax is valid
7
Templates: Crucial to C++ Generic Programming
Templates are used to parameterize classes and functions with different types Function templates do not require explicit declaration of parameterized types when called E.g., swap (i, j); Classes require explicit declaration of parameterized types when instantiated E.g., vector<int> v; Some compilers require that template definitions be included with declarations
8
Function Templates template <typename T> void swap(T & lhs, T & rhs) { T temp = lhs; lhs = rhs; rhs = temp; } int main () { int i = 3; int j = 7; long r = 12; long s = 30; swap (i, j); // i is now 7, j is now 3 swap (r, s); // r is now 30, s is now 12 return 0; The swap function template takes a single type parameter, T interchanges values of two passed arguments of the parameterized type Compiler instantiates the function template twice in main with type int for the first call with type long for the second call Note that the compiler infers the type each time swap is called Based on the types of the arguments
9
Another Function Template Example
STL-style linear search (based on Austern pp. 13): template <typename Iterator, typename T> Iterator find2 (Iterator first, Iterator last, const T & value) { while (first != last && *first != value) { ++first; } return first; Our first generic algorithm Searches any one-dimensional sequence of elements “Not found” is a normal result, does not throw exception Returns position last: just past the end of the range [first, last)
10
Class Templates Start with a simple declaration
template <typename T> class Array { public: Array(const int size); ~Array(); const int size () const; private: T * m_values; const int m_size; }; int main (int, char**) { Array <int> a(10); return 0; } Start with a simple declaration Which we’ll expand as we go Notice that parameterized type T is used within the class template Type of pointer to array When an instance is declared, must also explicitly specify the concrete type parameter E.g., int in function main (int, char**)
11
Class Templates and Inheritance
class ArrayBase { public: ArrayBase(const int size); ~ArrayBase(); const int size () const; protected: const int m_size; }; template <typename T> class Array : public ArrayBase { Array(const int size); ~Array(); private: T * m_values; Class templates can inherit from base classes Or class templates, but must relate type parameters to those of base Here, we’ve separated template and non-template code Non-template base class Template derived class Functions and variables that do not need to refer to type parameters go into the base class This is useful in many cases To avoid accidental type errors (ArrayBase doesn’t access T) To reduce program size (separate method definitions compiled for Array<int> Array<float> etc.)
12
Templates Introduce Interface Polymorphism
Can plug in any types for which template’s syntactic use is valid (get a compiler error if not) Templates apply to types that provide a common interface What needs to be common depends on the template Templates impose requirements on type parameters Each requirement consists of a C++ expression that must be valid for that types involved in that expression e.g., if a template’s code has Z z; Z must allow default construction Interface polymorphism still allows Liskov Substitution If S models a subset of the requirements modeled by T then wherever you need an S you can plug in a T Much more on this in the next several lectures
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.