1 / 59

CIS 4930 Application Development Using C++ Dr. Kun Suk Kim CISE Department, University of Florida

CIS 4930 Application Development Using C++ Dr. Kun Suk Kim CISE Department, University of Florida. Utilities. Caveat. Don’t use auto_ptr in a parameter list or as a return value if you don’t mean to transfer ownership template <class T> void bad_print(std::auto_ptr<T> p) {

mahdis
Download Presentation

CIS 4930 Application Development Using C++ Dr. Kun Suk Kim CISE Department, University of Florida

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. CIS 4930Application Development Using C++Dr. Kun Suk KimCISE Department, University of Florida Utilities

  2. Caveat • Don’t use auto_ptr in a parameter list or as a return value if you don’t mean to transfer ownership template <class T> void bad_print(std::auto_ptr<T> p) { if (p.get() == NULL) std::cout << “NULL”; else std::cout << *p; } // Oops, exiting deletes the object to which p refers std::auto_ptr<int> p(new int); *p = 42; bad_print(p); // Oops, deletes the memory to which p refers *p = 18; // RUNTIME ERROR

  3. Caveat • Transfer of ownership is not possible with constant references const std::auto_ptr<int> p(new int); *p = 42; bad_print(p); // COMPILE-TIME ERROR *p = 18; // OK

  4. Caveat • Container classes use constant reference to get values that they copy internally template <class T> void container::insert(const T& value) { … x = value; // assign or copy value internally … } container<std::auto_ptr<int> > c; const std::auto_ptr<int> p(new int); … c.insert(p); // ERROR …

  5. Caveat • Can’t change the ownership of a constant auto_ptr • Can change the value of the object to which it refers std::auto_ptr<int> f() { const std::auto_ptr<int> p(new int); // no ownership transfer possible std::auto_ptr<int> q(new int); // ownership transfer possible *p = 42; // OK, change value to which p refers bad_print(p); // COMPILE-TIME ERROR *p = *q; // OK, change value to which p refers p = q; // COMPILE-TIME ERROR return p; // COMPILE-TIME ERROR }

  6. Auto_ptrs as Members • Avoid resource leaks within a class • No longer need a destructor • Helps to avoid resource leaks that are caused by exceptions that are thrown during the initialization of an object • Note: destructors are called only if any construction is completed • If an exception occurs inside a constructor, destructors are only called for objects that have been fully constructed

  7. Auto_ptrs as Members ~ClassB() { delete ptr1; delete ptr2; } }; class ClassB { private: classA* ptr1; // pointer members classA* ptr2; public: // might cause resource leak if second new throws ClassB (ClassA val1, ClassA val2) : ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { } // might cause resource leak if second new throws ClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { } const ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; }

  8. Auto_ptrs as Members class ClassB { private: const std::auto_ptr<classA> ptr1; // auto_ptr members const std::auto_ptr<classA> ptr2; public: // no resource leak possible ClassB (ClassA val1, ClassA val2) : ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { } // no resource leak possible ClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { } const ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } // no destructor necessary };

  9. Examples template <class T> ostream& operator<< (ostream& strm, const auto_ptr<T>& p){ if (p.get() == NULL) strm << "NULL"; else strm << *p; return strm; } auto_ptr<int> p(new int(42)); auto_ptr<int> q; cout << "p: " << p << " q: " << q << endl; q = p; cout << "p: " << p << " q: " << q << endl; *q += 13; p = q; cout << "p: " << p << " q: " << q << endl; p: 42 q: NULL p: NULL q: 42 p: 55 q: NULL

  10. Examples template <class T> ostream& operator<< (ostream& strm, const auto_ptr<T>& p){ if (p.get() == NULL) strm << "NULL"; else strm << *p; return strm; } const auto_ptr<int> p(new int(42)); const auto_ptr<int> q(new int(0)); const auto_ptr<int> r; cout << "p: " << p << " q: " << q << " r: " << r << endl; *q = *p; // *r = *p; // ERROR: undefined behavior *p = -77; cout << "p: " << p << " q: " << q << " r: " << r << endl; // q = p; r = p; // ERROR at compile time p: 42 q: 0 r: NULL p: -77 q: 42 r: NULL

  11. Misusing auto_ptrs • Auto_ptrs cannot share ownership • Auto_ptrs are not provided for arrays • Auto_ptr calls delete instead of delete[] • Auto_ptrs are not “universal smart pointers” • Are not pointers for reference counting • Auto_ptrs don’t meet the requirements for container elements

  12. Requirements for Container Elements • Copyable by a copy constructor • Assignable by the assignment operator • Destroyable by a destructor • For some member functions of sequence containers, the default constructor must be available • For several operations, the test of equality with operator == must be defined • For associative containers, the operations of the sorting criterion must be provided by the elements

  13. Value Semantics or Reference Semantics • STL containers provide value semantics • Strengths: • Copying elements is simple • References are error prone • Dangling pointers, circular references • Weaknesses: • Copying elements might result in bad performance or may not even be possible • Managing the same object in several containers at the same time is not possible • To get reference semantics for STL containers you must write your own smart pointer class

  14. Implementing Reference Semantics template <class T> class CountedPtr { private: T* ptr; long* count; public: explicit CountedPtr (T* p=0) : ptr(p), count(new long(1)) { } CountedPtr (const CountedPtr<T>& p) throw() : ptr(p.ptr), count(p.count) { ++*count; } ~CountedPtr () throw() { dispose(); }

  15. Implementing Reference Semantics CountedPtr<T>& operator= (const CountedPtr<T>& p) throw() { if (this != &p) { dispose(); ptr = p.ptr; count = p.count; ++*count; } return *this; } T& operator*() const throw() { return *ptr; } T* operator->() const throw() { return ptr; } private: void dispose() { if (--*count == 0) { delete count; delete ptr; } } };

  16. Implementing Reference Semantics void printCountedPtr (CountedPtr<int> elem){ cout << *elem << ' '; } int main(){ static int values[] = { 3, 5, 9, 1, 6, 4 }; typedef CountedPtr<int> IntPtr; deque<IntPtr> coll1; list<IntPtr> coll2; for (int i=0; i<sizeof(values)/sizeof(values[0]); ++i) { IntPtr ptr(new int(values[i])); coll1.push_back(ptr); coll2.push_front(ptr); } for_each (coll1.begin(), coll1.end(), printCountedPtr); cout << endl;

  17. Implementing Reference Semantics for_each (coll2.begin(), coll2.end(), printCountedPtr); cout << endl << endl; *coll1[2] *= *coll1[2]; (**coll1.begin()) *= -1; (**coll2.begin()) = 0; for_each (coll1.begin(), coll1.end(), printCountedPtr); cout << endl; for_each (coll2.begin(), coll2.end(), printCountedPtr); cout << endl; } 3 5 9 1 6 4 4 6 1 9 5 3 -3 5 81 1 6 0 0 6 1 81 5 -3

  18. Auxiliary Functions • Three auxiliary functions • The selection of minimum and maximum of two values • The swapping of two values • Defined in header file <algorithm>

  19. Processing the Minimum and Maximum • If both values are equal, generally the first element gets returned • It is not good programming style to rely on this namespace std { template <class T> inline const T& min (const T& a, const T& b) { return b < a ? b : a; } template <class T> inline const T& max (const T& a, const T& b) { return a < b ? b : a; } }

  20. Processing the Minimum and Maximum • Functions with the comparison criterion as an additional argument • A function or a function object namespace std { template <class T, class Compare> inline const T& min (const T& a, const T& b, Compare comp) { return comp(b,a) ? b : a; } template <class T , class Compare> inline const T& max (const T& a, const T& b, Compare comp) { return comp(a,b) ? b : a; } }

  21. Processing the Minimum and Maximum • The definition of min() and max() require that both types match int i; long l; … l = std::max(i,l); // ERROR: argument types don’t match • Should qualify explicitly the type of arguments l = std::max<long>(i,l); // OK

  22. Swapping Two Values • The call std::swap(x,y) is possible only if • The copy constructions and assignments inside the swap function are possible namespace std { template <class T> inline void swap (T& a, T& b) { T tmp(a); // copy construction a = b; // assignment b = tmp; // assignment } }

  23. Swapping Two Values • Swap() enables to provide special implementations for more complex types • By using template specialization or function overloading • The containers have the same type and the source is no longer used • Swaps only internal data of the containers • Constant complexity • Assignment of containers • copy all elements of the source container and remove all old elements in the destination container • Linear complexity

  24. Swapping Two Values class MyContainer { private: int* elems; int numElems; public: void swap (MyContainer& x) { std::swap(elems, x.elems); std::swap(numElems, x.numElems); } … }; • overloaded global swap() for this type Inline void swap(MyContainer& c1, MyContainer& c2) { c1.swap(c2); }

  25. Supplementary Comparison Operators • Four template functions • Define comparison operators !=, >, <=, and >= • Defined in <utiltiy> namespace std { namespace rel_ops { template <class T> inline bool operator!= (const T& x, const T& y) { return !(x == y); } template <class T> inline bool operator> (const T& x, const T& y) { return y < x; } template <class T> inline bool operator<= (const T& x, const T& y) { return !(y < x); } template <class T> inline bool operator>= (const T& x, const T& y) { return !(x < y); } } }

  26. Supplementary Comparison Operators • To use them, only define operators < and == #include <utility> class X { public: bool operator== (const X& x) const; bool operator< (const X& x) const; … } void foo() { using namespace std::rel_ops; // make !=, >, >=, and <= available X x1, x2; … if (x1 != x2) { … } if (x1 > x2) { … } }

  27. Numeric Limits • Numeric type have platform-dependent limits • Template numeric_limits • Replace and supplement the ordinary preprocessor constants of C • Offers more type safer • Enables a programmer to write templates that evaluate these limits • C constants are available • Integer types in <climits> • Floating-point types in <cfloat>

  28. Minimum Size of Built-in Types

  29. Class numeric_limits<> • A general template provides the default numeric values for any type • No specialization for numeric limits as default for any type namespace std { template <class T> class numeric_limits { public: static const bool is_specialized = false; … }; }

  30. Class numeric_limits<> • Specializations of the template define the numeric limits for each numeric type namespace std { template<> class numeric_limits<int> { public: static const bool is_specialized = true; static int min() throw() { return –2147483648; } static int max() throw() { return 2147483647; } static const int digits = 31; … }; }

  31. Class numeric_limits<> • The general numeric_limits template and its specializations are provided in the head <limits> • The specializations are provided for any fundamental type • bool, char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsinged long, float, double, and long double

  32. Specialization of Numeric Limits for Type float namespace std { template<> class numeric_limits<float> { public: static const bool is_specialized = true; inline static float min() throw() { return 1.17549435E-38F; } inline static float max() throw() { return 3.40282347E+38F; } static const int digits = 24; static const int digits10 = 6; static const bool is_signed = true; static const bool is_integer = false; static const bool is_exact = false; static const bool is_bounded = true; static const bool is_modulo = false; static const bool is_iec559 = true;

  33. Specialization of Numeric Limits for Type float static const int radix = 2; inline static float epsilon() throw() { return 1.19209290E-07F; } static const float_round_style round_style = round_to_nearest; inline static float round_error() throw() { return 0.5F; } static const int min_exponent = -125; static const int max_exponent = +128; static const int min_exponent10 = -37; static const int max_exponent10 = +38; static const bool has_infinity = true; inline static float infinity() throw() { return …; } static const bool has_quiet_NaN = true; inline static float quiet_NaN() throw() { return …; } static const bool has_signaling_NaN = true; inline static float signaling_NaN() throw() { return …; }

  34. Specialization of Numeric Limits for Type float static const float_denorm_style has_denorm = denorm_absent; static const bool has_denorm_loss = false; inline static float denorm_min() throw() { return min(); } static const bool traps = true; static const bool tinyness_before = true; }; }

  35. Example of Using numeric_limits<> cout << "short: " << numeric_limits<short>::max() << endl; cout << "int: " << numeric_limits<int>::max() << endl; cout << "long: " << numeric_limits<long>::max() << endl; cout << "float: " << numeric_limits<float>::max() << endl; cout << "double: " << numeric_limits<double>::max() << endl; cout << "long double: " << numeric_limits<long double>::max() << endl; cout << "is_signed(char): " << numeric_limits<char>::is_signed << endl; cout << "is_specialized(string): " << numeric_limits<string>::is_specialized << endl; short: 32767 int: 2147483647 long: 2147483647 float: 3.40282e+038 double: 1.79769e+308 long double: 1.79769e+308 is_signed(char): 1 is_specialized(string): 0

  36. Definitions in <cstddef>

  37. Definitions in <cstdlib>

  38. CIS 4930Application Development Using C++Dr. Kun Suk KimCISE Department, University of Florida Virtual Functions

  39. Objectives • Virtual Function Basics • Late binding • Implementing virtual functions • When to use a virtual function • Abstract classes and pure virtual functions • Pointers and Virtual Functions • Extended type compatibility • Downcasting and upcasting • C++ ‘under the hood’ with virtual functions

  40. Virtual Function Basics • Polymorphism • Associating many meanings to one function • Virtual functions provide this capability • Fundamental principle of object-orientedprogramming! • Virtual • Existing in ‘essence’ though not in fact • Virtual Function • Can be ‘used’ before it’s ‘defined’

  41. Figures Example • Best explained by example: • Classes for several kinds of figures • Rectangles, circles, ovals, etc. • Each figure an object of different class • Rectangle data: height, width, center point • Circle data: center point, radius • All derive from one parent-class: Figure • Require function: draw() • Different instructions for each figure

  42. Figures Example 2 • Each class needs different draw function • Can be called ‘draw’ in each class, so:Rectangle r;Circle c;r.draw(); //Calls Rectangle class’s drawc.draw(); //Calls Circle class’s draw • Nothing new here yet…

  43. Figures Example: center() • Parent class Figure contains functionsthat apply to ‘all’ figures; consider:center(): moves a figure to center of screen • Erases first, then re-draws • So Figure::center() would use function draw()to re-draw • Complications! • Which draw() function? • From which class?

  44. Figures Example: New Figure • Consider new kind of figure comes along:Triangle class derived from Figure class • Function center() inherited from Figure • Will it work for triangles? • It uses draw(), which is different for each figure! • It will use Figure::draw()  won’t work for triangles • Want inherited function center() to use functionTriangle::draw() NOT function Figure::draw() • But class Triangle wasn’t even WRITTEN whenFigure::center() was! Doesn’t know ‘triangles’!

  45. Figures Example: Virtual! • Virtual functions are the answer • Tells compiler: • “Don’t know how function is implemented” • “Wait until used in program” • “Then get implementation from objectinstance” • Called late binding or dynamic binding • Virtual functions implement late binding

  46. Virtual Functions: Another Example • Bigger example best to demonstrate • Record-keeping program for automotiveparts store • Track sales • Don’t know all sales yet • First only regular retail sales • Later: Discount sales, mail-order, etc. • Depend on other factors besides just price, tax

  47. Virtual Functions: Auto Parts • Program must: • Compute daily gross sales • Calculate largest/smallest sales of day • Perhaps average sale for day • All come from individual bills • But many functions for computing bills willbe added ‘later’! • When different types of sales added! • So function for ‘computing a bill’ will bevirtual!

  48. Class Sale Definition • class Sale{public: Sale(); Sale(double thePrice); double getPrice() const;virtual double bill() const; double savings(const Sale& other) const;private: double price;};

  49. Member Functions savings and operator < • double Sale::savings(const Sale& other) const{ return (bill() – other.bill());} • bool operator < ( const Sale& first, const Sale& second){ return (first.bill() < second.bill());} • Notice BOTH use member function bill()!

  50. Class Sale • Represents sales of single item with noadded discounts or charges. • Notice reserved word ‘virtual’ indeclaration of member function bill • Impact: Later, derived classes of Sale candefine THEIR versions of function bill • Other member functions of Sale will useversion based on object of derived class! • They won’t automatically use Sale’s version!

More Related