1 / 12

Motivation for Generic Programming in C++

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

rjoy
Télécharger la présentation

Motivation for Generic Programming in C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  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 native container type (array) user-defined iterator type native iterator type (pointer) callable object calls to STL algorithms Iterator constructor calls #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 0 1 2 3 4 5 6 7 8 9 (ascending sort) sort (numbers, numbers + array_dimension); copy (numbers, numbers + array_dimension, ostream_iterator<int>(cout, " ")); cout << endl; // prints out 9 8 7 6 5 4 3 2 1 0 (descending sort) sort (numbers, numbers + array_dimension, greater<int>()); copy (numbers, numbers + array_dimension, ostream_iterator<int>(cout, " ")); cout << endl; // prints out 5 6 2 1 9 4 7 8 3 0 (shuffled) random_shuffle (numbers, numbers + array_dimension); copy (numbers, numbers + array_dimension, ostream_iterator<int>(cout, " ")); cout << endl; return 0; }

  3. Decoupling Data Types from Algorithms • The C++ 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 • Callable objects plug in functions to modify algorithms • Containers include arrays, vector, list, set • Iterators include pointers, ostream_iterator • Algorithms include sort, copy, random_shuffle • Callable objects 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) • Callable objects decouple algorithms’ 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 callable 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. 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 Function Templates

  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. 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**) Class Templates

  11. class ArrayBase { public: ArrayBase(const int size); ~ArrayBase(); const int size () const; protected: const int m_size; }; template <typename T> class Array : public ArrayBase { public: 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.) Class Templates and Inheritance

  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

More Related