CS11 – Advanced C++ Spring 2012-2013 Lecture 7Today’s Topics n All about casting in C++ q Implicit casting q explicit keyword on constructors q Explicit casting in C++ q mutable keyword and constImplicit Type-Conversions n C++ has implicit type-conversions for primitive types q You don’t explicitly cast from one type to the other q They just happen, without warning! J n Promotions q Value is preserved, no information is lost q Examples: char → int, bool → int n Conversions q Value may actually change or become invalid q Examples: double → char, double → float n Beware! Compiler doesn’t give you much help!Conversion of User-Defined Types n Can define implicit conversion ops for user-types class Rational { public: Rational(int num, int denom); ... // Convert from Rational to double operator double() const; }; q Provides implicit conversion from Rational to double Rational r(1, 2); // r is 1/2 double d = 0.5 * r; q r is converted to double, then multiplication is performedUnexpected Results! n Now you want to print Rational values with << q Print them as “num/denom” q …but, you forgot to implement << n You write this code: Rational r(1, 2); cout << r; q You expect this to print 1/2 q (Or, actually, to not compile since you didn’t implement <<) q But it does compile, and it prints out 0.5 q Not too surprising, just subtle.Crafty Compilers n Compiler sees no << for Rational q “But Rational can be converted to double, and double can be output with <<…” n Problem: q Implicit conversion operations can produce unexpected or undesirable results! q Violates the “Law of Least Surprise” n Moral: q Be very careful with implicit conversion operations q Better yet, don’t use them: double Rational::asDouble() { ... }More Implicit Conversion Options n Single-argument constructors also enable implicit conversions in C++ n Example: Rational(int num = 0, int denom = 1); q Defines default values for arguments q Also allows ints to be converted to Rationals Rational r1(3); // r1 = 3/1 Rational r2 = 5; // r2 = 5/1 r1 = 6; // r1 = Rational(6) q Compiler figures out the conversions to use!Another Implicit Conversion Example n An integer-array class: class Array { ... public: Array(int size); int & operator[](int index); bool operator==(const Array &) const; bool operator!=(const Array &) const; };More Unexpected Results n Want to compare two arrays: Array a(10), b(10); ... for (int i = 0; i < 10; i++) { if (a == b[i]) { ... // Do stuff! } } n Oops; meant to type a[i] == b[i] q But, the code compiles! n What happens: q Compiler guesses this: a == Array(b[i]) q Wrong, not to mention terribly inefficient!Disallowing Implicit Conversions n Compiler should complain in these cases q Don’t want it to make up stuff that compiles, but that you didn’t mean! n Enter the explicit keyword q Added to C++ specifically because of these issues q Can declare constructors to be explicit q C++ won’t use them for implicit conversions n Example: explicit Array(int size);Default Parameters and explicit n If constructor can take just one argument, it will be used as an implicit conversion q …even multi-arg constructors with default values n Rational example again: Rational(int num = 0, int denom = 1); q Defines default values for arguments q Also provides implicit conversion from int n Can also use explicit here: explicit Rational(int num = 0, int denom = 1); q No longer allows implicit conversions from intExplicit Casts in C and C++ n C has one explicit cast operator for everything q Can convert between related types n e.g. double to int q Can cast pointers to different types n e.g. void* to float*, or float* to char* q Can cast pointers to int, and vice versa q Can cast away const-ness q Many unsafe or potentially unsafe scenarios! n C++ provides four explicit casting operators q Breaks down different kinds of casts into explicit operations q Provides more type-safety checks at compile-time, run-time q Easier for programmers to understand, tooC++ Cast Operations n const_cast q Casts away const-ness n static_cast q Performs safe conversions between related types, using static (compile-time) type information n dynamic_cast q Uses runtime type information to safely cast down or across class hierarchies n reinterpret_cast q For all the dangerous stuff.Removing const Constraints n const_cast removes const constraints q T const_cast<T>(const T value) n Not for changing the type of value ! q Using const_cast to change types will not compile n Examples: void output(SpecialWidget *psw); const SpecialWidget csw; output(&csw); // COMPILE ERROR output(const_cast<SpecialWidget*>(&csw)); // OK Widget *pw = new SpecialWidget(); output(const_cast<SpecialWidget*>(pw)); // COMPILE ERRORCached Values and const n const_cast sometimes used when objects cache temporary values that are expensive to compute class Date { ... string cache; bool cacheValid; void updateCacheVals(); // sets cache value public: ... string stringRep() const; }; q stringRep() returns a string version of the date value q Cache the result, as long as date value doesn’t changeCached Values and const (2) n Implementation of stringRep() string Date::stringRep() const { if (!cacheValid) { // Type of this is "const Date *", so // cast to non-const Date * Date *mut = const_cast<Date *>(this); mut->updateCacheVals(); mut->cacheValid = true; } return cache; } q this is const because the member function is const q Must cast away const to update cached values!Cached Values and const (3) n That solution isn’t very elegant. n Also, it might not actually work! q If original variable is declared as const, casting away const is not guaranteed to work on all implementations n e.g. compiler or OS might store variable in read-only
View Full Document