CS 251 INTERMEDIATE SOFTWARE DESIGN SPRING 2011 C ++ Basics Review part 2 Auto pointer, templates, STL algorithms
AUTO POINTER (AUTO_PTR) //Example showing a bad situation with naked pointers void MyFunction() { MyClass* ptr( new MyClass); /*.some more code.*/ delete ptr; ptr=null; } delete ptr might not happen memory leak! 2
AUTO POINTER (AUTO_PTR) Solution : standard auto_ptr (read auto-pointers). part of C++ own the dynamically allocated object perform the automatic cleanup Now, lets use an auto_ptr and see how it makes life easy: //Previous situation tackled using auto_ptr void MyFunction() { auto_ptr<myclass> ptr(new MyClass); /*.some more code.*/ } 3
AUTO POINTER (AUTO_PTR) No need to explicitly call a delete Possible transfer of the ownership to another auto_ptr, to another function or free/reset it to use it again. //Example showing transfer of ownership. auto_ptr<myclass> autoptr1(new MyClass); auto_ptr<myclass> autoptr2; autoptr2 = autoptr1; //Now autoptr2 owns the pointer & autoptr1 doesn't autoptr1->myfunction(); // ERROR!!! //autoptr1, now is a null pointer 4
AUTO POINTER (AUTO_PTR) Potential problem : copies of auto_ptr are not equivalent 1. Let s consider STL containers or some generic algorithms/functions like sort. auto_ptr copies are not equivalent due to the strict ownership semantics. ( what about a sort algorithm that holds a copy of an element as a pivot?) 2. Initializing (setting/assigning) another auto_ptr object with an object held by the vector. 3. What if there is a member function that returns the contained vectors element is returned by value? 4. What if you make another copy of the vector? Or for that matter, create some other container out of its elements? 5. Use of member functions of vector like - constructor, assign, insert, resize - will they succeed? Will they definitely succeed? 5
AUTO POINTER (AUTO_PTR) Copy constructor and copy assignment take a reference to a non-const right hand side objects STL containers' insert() takes a reference to a const and hence auto_ptr cannot be used. It will just not allow you to create a copy of auto_ptr inside the vector (standard containers) members. That will cause you compilation errors on a compliant compiler. But still, as a strict guideline we should not use the auto_ptr wherever a copy of theirs is expected to be made. 6
AUTO POINTER: EXAMPLE #include <iostream> #include <memory> using namespace std; int main(int argc, char **argv) { int *i = new int; auto_ptr<int> x(i); auto_ptr<int> y; y = x; } cout << x.get() << endl; cout << y.get() << endl; // Print NULL // Print non-null address i 7
AUTO POINTER: EXPLANATION OF EXAMPLE This code will print a NULL address for the first auto_ptr object and some non-null address for the second, showing that the source object lost the reference during the assignment (=). The raw pointer i in the example should not be deleted, as it will be deleted by the auto_ptr that owns the reference. In fact, new int could be passed directly into x, eliminating the need for i. Notice that the object pointed by an auto_ptr is destroyed using operator delete; this means that you should only use auto_ptr for pointers obtained with operator new. This excludes pointers returned by malloc/calloc/realloc and arrays, which are allocated by operator new[] and must be deallocated by operator delete[]. 8
STACK EXAMPLE SCOPED ARRAY Example Scoped_array.h StackScopedArry.h StackScopedArry.cpp StackScopedArrymain.cpp Purpose: Extend the auto_ptr to arrays The deletion of the stack is guaranteed with the destruction of Scoped_array. 9
TEMPLATE What exactly are templates for, and why learn them? Limited Generic Programming (polymorphism) Some functions have the same semantic meaning for some if not all) data types. For instance, a function print() should display a sensible representation of anything passed in. Ideally, it shouldn t need to be rewritten for each possible type. Less repetitive code Code that only differs in the data type it handles does not have to be rewritten for each and every data type you want to handle. It s easier to read and maintain since one piece of code is used for everything. 10
EXAMPLE: A SWAP FUNCTION Problem: Oftentimes, it is nice to be able to swap the values of two variables. This function s behavior is similar for all data types. Templated functions let you do that in most cases without any syntax changes. Stupid method write an overloaded function for each type Swap for integers void swap(int &a, int &b) { int c = a; a = b; b = c; } Swap for double void swap(double &a, double &b) { double c = a; a = b; b = c; } Template method write one templated function template <typename T> void swap(t &a, T &b) { T c = a; a = b; b = c; } This function can be used with any type that supports assignment and can be passed in as a non-const reference. 11
TEMPLATE SYNTAX: SWAP DISSECTED The template< > line states that everything in the following declaration or definition is under the subject of the template. (In this case, the definition is the function swap) In here goes a list of placeholders variables. In almost all cases, they will be specified with either the typename or class keywords. These two keywords are almost equivalent. Template behavior: There are two ways to use templates, implicit and explicit specialization. Explicit specialization always works. Implicit sometimes works. template <typename T> void swap(t &a, T &b) { T c = a; a = b; b = c; } Placeholder variables have one value within each template declaration. Think of them as being replaced by whatever type you specify the template to be. 12
TEMPLATE: USING IT Using a template To use a template, one has to specialize it. This is why it isn t quite a generic function. It does static polymorphism. It morphs itself to the right type during preprocess time. Syntax To explicitly specialize a template, write its name with the arguments for the placeholder variables in angle brackets. Example: double d1 = 4.5, d2 = 6.7; swap<double>(d1, d2); Templates however can auto-sense its placeholder values if all information about what the placeholders represent can be inferred from its context (arguments, and for member functions, the associated class instance). This is called implicit specialization. In the previous case, the compiler is smart enough to figure out that T is a double even without the explicit <double> since the arguments are doubles. Thus this shorthand works: Example: swap(d1, d2); 13
HOW THEY WORK: PREPROCESSOR Templates do not exist! (there is no spoon) Templates are a preprocessor construct. They are cookiecutters with which the preprocessor generates real C++ code. When a template is used, (that is, specialized, implicitly or explicitly), it get instantiated. The instantiation tells the preprocessor to create a version of the template where each placeholder is replaced by its specialization. At this point, the specific version of the template comes into existence and can be compiled. It does not exist otherwise! In a very real way, a template just does a search and replace for each type you specialize the template for. In essence, you are doing the same as writing a bunch of overloaded functions. It s just done for you, behind your back. 14
HOW THEY WORK: CONSEQUENCES Problem: Templates are resolved in the preprocessing stage, they don t exist to the compiler until they get instantiated. This is the balance between trying to make templates work transparently, and trying to make things efficient. Effects: Template code will not get compiled until used (and instantiated). Thus, the compiler will not catch syntax errors until the template is used. A specialization instantiates all relevant templates before it. If a template appears after a specialization, it doesn t get instantiated and is not compiled. Conclusion: To make things work, all relevant template definitions must appear before at least one specialization. Otherwise, parts of the template will not get instantiated and compiled. Solution: #include MyClass.cpp in the.h file! 15
CLASS TEMPLATE Syntax: Templated classes basically follow same syntax as templated functions. Notice the only addition to the class definition is the line: template <typename T> Within the definition block, the placeholder has can be used as a data type. When the template is specialized, it takes on the value of the specialization. #ifndef MATRIX_H #define MATRIX_H template <typename T> class Matrix { public: Matrix(int rows, int cols); Matrix(const Matrix &other); virtual ~Matrix(); Matrix& operator=(const Matrix &rhs); T* operator[](int i); int getrows() const; int getcols() const; protected: void copy(const Matrix &other); private: Matrix(); int m_rows; int m_cols; T *m_linarray; }; #endif /* MATRIX_H */ 16
CLASS TEMPLATE Syntax Templated classes must be explicitly specialized. Thus, to create a 2 dimensional Matrix of doubles using the last example, the syntax would be: Matrix<double> m(3,3); This specialization during declaration in reality creates a new type namely Matrix<double>. This should be thought of as its own type, separate from any other specialization of Matrix (so it is different from Matrix<int>, or Matrix<Foo>, etc.) At this point, the instance behaves as any other instantiated type at least for compilation. 17
TEMPLATE FUNCTION SPECIALIZATION In some cases it is possible to override the template-generated code by providing special definitions for specific types. This is called template specialization. The following example demonstrates a situation where overriding the template generated code would be necessary: #include <iostream> using namespace std ; //max returns the maximum of the two elements of type T, where T is a //class or data type for which operator> is defined. template <class T> T max(t a, T b) { return a > b? a : b ; } int main() { cout << "max(10, 15) = " << max(10, 15) << endl ; cout << "max('k', 's') = " << max('k', 's') << endl ; cout << "max(10.1, 15.2) = " << max(10.1, 15.2) << endl ; cout << "max(\"aladdin\", \"Jasmine\") = " << max("aladdin", "Jasmine") << endl ; return 0 ; } 18
TEMPLATE FUNCTION SPECIALIZATION Program Output? max(10, 15) = 15 max('k', 's') = s max(10.1, 15.2) = 15.2 max("aladdin", "Jasmine") = Aladdin Why? The function call max("aladdin", "Jasmine") causes the compiler to generate code for max(char*, char*), which compares the addresses of the strings! To correct special cases like these or to provide more efficient implementations for certain types, one can use template specializations. 19
TEMPLATE FUNCTION SPECIALIZATION //max returns the maximum of the two elements template <class T> T max(t a, T b) { return a > b? a : b ; } // Specialization of max for char* template <> char* max(char* a, char* b) { return strcmp(a, b) > 0? a : b ; } Program Output max(10, 15) = 15 max('k', 's') = s max(10.1, 15.2) = 15.2 max("aladdin", "Jasmine") = Jasmine 20
STACK EXAMPLE: PARAMETERIZED TYPE Note: Exception handling has been removed to simplify the examples. It is very important to include exception handling! The following files are for a parameterized type Stack class StackParam.h StackParam.cpp StackParamMain.cpp Benefits Permits multiple stacks of different data types. 21
STACK EXAMPLE: PARAMETERIZED TYPE CLASS Another parameterized type Stack Class that does not require dynamic memory, but the size of the stack must be constant. StackParam2.h StackParam2.cpp StackParamMain2.cpp 22
STACK EXAMPLE: PARAMETERIZED TYPE CLASS Problems Changes to the implementation will require recompilation and relinking of clients Extensions will require access to the source code. Potential Solutions Combine inheritance with dynamic binding to completely decouple interface from implementation and binding time. Must use C++ abstract base classes 23
STACK EXAMPLE: ABSTRACT BASE CLASS Use virtual functions StackVir.h Node.h L_Stack.h L_Stack.cpp V_Stack.h V_Stack.cpp StackVirMain.cpp Benefits The abstract base class permits the development of code that does not depend on the stack implementation. 24
SUMMARY A major contribution of C++ is its support for defining abstract data types (ADTs) and for generic programming. For example, classes, parameterized types and exception handling. For some systems, C++ s ADT support is more important than using the OO features of the language For other systems, the use of C++ s OO features is essential to build highly flexible and extensible software For example, inheritance, dynamic binding and RTTI 25