///////////////////////////////////////////////////////////////////// // casts.cpp - demonstrates affects of casting // // // // Jim Fawcett, CSE687 - Object Oriented Design, Spring 2002 // ///////////////////////////////////////////////////////////////////// #include #include #include using namespace std; /* Casts: ====== - static_cast: calls promotion constructor defined by toType or cast operator defined by fromType. toType toObject = static_cast(fromObject); - reinterpret_cast: treats existing object as a different type. Does not change any bits in the object referenced. toType *ptoType = reinterpret_cast(&fromObject); - const_cast: allow operation not marked as const on const object. Don't do this if the operation actually changes the state of constTypeObject. type *pType = const_cast(&constTypeObject); pType->nonConstOperation(); - dynamic_cast: safe cast down inheritance hierarcy Derived *pDerived = dynamic_cast(pBase); if(pDerived) pDerived->derivedMember(); You need to turn on RTTI in: Project\Settings\C/C++\Category: C++ Language\Enable RTTI Temporaries: ============ - pass or return by value: create temporary on function stack frame void funct1(type instance) { ... } type funct2() { type typeObject; .... return typeObject; } - use type without identifier in an expression type().memberFunction(); type(someArgument).anotherMemberFunction(); */ // /////////////////////////////////////////////////////////////// // global utility functions // - these are simply utility functions that are used to // display and format output. They do not illustrate // any significant C++ details // string title(const char *title, char underline='-') { string temp = string("\n ") + string(title); temp += string("\n ") + string(strlen(title)+2, underline); return temp; } void trace(const char *msg) { cout << "\n --" << msg; } // /////////////////////////////////////////////////////////////// // declaration of demonstration Base class // - this class declares both promotion and cast operations // class Base { public: Base(); // void ctor Base(const Base &b); // copy ctor Base(const string &s); // promotion ctor virtual ~Base(); // dtor Base& operator=(const Base &b); // assignment operator const char* (); // cast operator virtual void speak(); // virtual function void argue(); // non-virtual function private: string _s; }; Base::Base() : _s("default Base string") { trace("base default ctor"); } Base::Base(const Base &b) : _s(b._s) { trace("base copy ctor"); } Base::Base(const string &s) : _s(s) { trace("base promotion ctor"); } Base::~Base() { trace("Base dtor"); } Base& Base::operator =(const Base &b) { trace("base assignment"); if(this == &b) return *this; _s = b._s; return *this; } Base::operator const char* () { trace("base cast operator"); return _s.c_str(); } void Base::speak() { cout << "\n Base instance here!"; } void Base::argue() { cout << "\n Base instance arguing"; } // /////////////////////////////////////////////////////////////// // declaration of demonstration Derived class // class Derived : public Base { public: Derived(); // void ctor Derived(const Derived &d); // copy ctor Derived(const string &s); // promotion ctor virtual ~Derived(); // dtor Derived& operator=(const Derived &d); // assignment operator string (); // cast to string virtual void speak(); // overriding base virtual virtual void newSpeak(); // extending base interface void argue(); // non-virtual private: string _s; }; Derived::Derived() : Base("Base string from derived"), _s("default derived string") { trace("Derived void ctor"); } Derived::Derived(const Derived &d) : Base(d), _s(d._s) { trace("Derived copy ctor"); } Derived::Derived(const string &s) : Base("Base string from derived"), _s(s) { trace("Derived promotion ctor"); } Derived::~Derived() { trace("Derived dtor"); } Derived& Derived::operator=(const Derived &d) { trace("Derived assignment"); if(this == &d) return *this; static_cast(*this) = d; // use Base assignment _s = d._s; return *this; } // Derived::operator string () { trace("Derived cast operator"); string temp = _s; temp += " - "; temp += static_cast(*this); // use base cast return temp.c_str(); } void Derived::speak() { cout << "\n Derived instance here!"; } void Derived::newSpeak() { cout << "\n Derived instance speaking through extended interface"; } void Derived::argue() { cout << "\n Derived instance arguing"; } // /////////////////////////////////////////////////////////////// // global functions using Base class objects // - passByValue illustrates one way in which temporary // objects are created // - returnByValue illustrates another way that temporaries // are constructed // - passByConstReference illustrates the need for and // use of const_cast // void passByValue(Base b) { trace("passByValue function"); b.speak(); } Base returnByValue() { trace("returnByValue function"); Base temp; return temp; } void passByConstReference(const Base &b) { // b.speak() fails to compile because b is const Base& // and speak() is not declared as const trace("passByConstReference function"); Base *pBase = const_cast(&b); pBase->speak(); } // //----< entry point >------------------------------------------ void main() { cout << endl << title("Demonstrating Cast Operators with Base class objects",'='); /////////////////////////////////////////////////////// // old style casts are hard to spot, and result // in one of two distinct operations: // - coercion by calling a promotion constructor or // cast operator, // - interpreting one type as another without creating // a new object /////////////////////////////////////////////////////// cout << endl << title("old-style cast with base object"); Base b1; cout << "\n " << (const char*)b1; /////////////////////////////////////////////////////// // static cast creates a new object by calling an // appropriate constructor or cast operator /////////////////////////////////////////////////////// cout << endl << title("static_casts with base objects"); // calling Base promotion ctor b1 = static_cast(string("using static cast")); // calling Base cast operator cout << "\n " << static_cast(b1); //////////////////////////////////////////////////////// // the first of the previous casts can be done // implicitly as shown below //////////////////////////////////////////////////////// b1 = string("using static cast implicitly then explicitly"); cout << "\n " << static_cast(b1); b1 = string("called copy ctor"); Base b2 = b1; cout << "\n " << static_cast(b2); // /////////////////////////////////////////////////////// // Reinterpret casts are dangerous beasts - they create // no new object, simply applying the operations of one // type to an instance of another type. // // This is almost always HIGH EVIL! /////////////////////////////////////////////////////// cout << endl << title("reinterpret_cast with base object"); // causes base object to speak like a derived object Derived *pD = reinterpret_cast(&b2); // pD->newSpeak(); // the statement above will compile but crash when executed pD->argue(); cout << " - this is a lie!"; /////////////////////////////////////////////////////////////////// // this illustrates one, possibly useful application of // reinterpret_cast /////////////////////////////////////////////////////////////////// cout << endl << title("reinterpret cast used to look at bytes"); cout << "\n bytes from double:\n "; double dbl = 3.1415927; char *byte = reinterpret_cast(&dbl); int i; for(i=0; i(d); d = string("promoting string to derived"); cout << "\n " << static_cast(d); /////////////////////////////////////////////////////////////////// // polymorphic operations depend on casting derived class object // pointers or references to statically typed base class pointers // or references /////////////////////////////////////////////////////////////////// cout << endl << title("polymorphic operations"); Base *pBase = &b1; pBase->speak(); pBase = &d; // implicit cast preserves polymorphism pBase->speak(); pBase = static_cast(&d); pBase->speak(); // explicit cast has same effect Base &rBase1 = b1; rBase1.speak(); Base &rBase2 = d; rBase2.speak(); // polymorphic Base &rBase3 = static_cast(d); // polymorphic rBase3.speak(); Base &rBase4 = static_cast(d); // not polymorphic since a rBase4.speak(); // new base object is created // pBase->newSpeak(); // would fail to compile - not part of Base protocol cout << endl << title("non-polymorphic operation using dynamic_cast"); Derived *pDerived = dynamic_cast(pBase); if(pDerived) pDerived->newSpeak(); cout << "\n\n"; }