Difference between revisions of "Intermediate C++ Game Programming Tutorial 19"
From Chilipedia
(→Video Timestamp Index) |
(→Video Timestamp Index) |
||
(13 intermediate revisions by 2 users not shown) | |||
Line 11: | Line 11: | ||
[https://youtu.be/g-NGBFCn3co Tutorial 19] | [https://youtu.be/g-NGBFCn3co Tutorial 19] | ||
<div class="mw-collapsible mw-collapsed"><br /> | <div class="mw-collapsible mw-collapsed"><br /> | ||
− | * Using <code>dynamic_cast< | + | * Using <code>dynamic_cast<DerivedType>(pBaseType)</code> to determine to what derived type a pointer to a polymorphic base type is actually pointing [https://youtu.be/g-NGBFCn3co?t=54s 0:54] |
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
** A dynamic cast can be used on pointers and on references | ** A dynamic cast can be used on pointers and on references | ||
Line 24: | Line 24: | ||
** You have a pointer <code>BaseClass* ptr = new DerivedClass</code> to the polymorphic base class that you know points to the derived class (the "dynamic type") | ** You have a pointer <code>BaseClass* ptr = new DerivedClass</code> to the polymorphic base class that you know points to the derived class (the "dynamic type") | ||
** To call the derived class' member function, you can use a static cast to DerivedClass pointer <br /><code>static_cast<DerivedClass*>(ptr)->Foo();</code> | ** To call the derived class' member function, you can use a static cast to DerivedClass pointer <br /><code>static_cast<DerivedClass*>(ptr)->Foo();</code> | ||
− | ** ... or a static cast to DerivedClass reference <br /><code>static_cast<DerivedClass&>(*ptr).Foo();</code></div> | + | ** ... or a static cast to DerivedClass reference <br /><code>static_cast<DerivedClass&>(*ptr).Foo();</code> |
+ | </div> | ||
* Using <code>const_cast<>()</code> to remove a const from a reference or pointer (yes, you read that right) [https://youtu.be/g-NGBFCn3co?t=7m27s 7:27] | * Using <code>const_cast<>()</code> to remove a const from a reference or pointer (yes, you read that right) [https://youtu.be/g-NGBFCn3co?t=7m27s 7:27] | ||
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
Line 33: | Line 34: | ||
* Overview of different types of casts [https://youtu.be/g-NGBFCn3co?t=9m30s 9:30] | * Overview of different types of casts [https://youtu.be/g-NGBFCn3co?t=9m30s 9:30] | ||
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
− | ** static_cast | + | ** <code>static_cast<>()</code> |
*** converts types (int->float etc.) | *** converts types (int->float etc.) | ||
*** casts pointers/references within an inheritance hierarchy (no checking of the validity of the cast!) | *** casts pointers/references within an inheritance hierarchy (no checking of the validity of the cast!) | ||
− | ** dynamic_cast | + | ** <code>dynamic_cast<>()</code> |
*** casts pointers/references within an inheritance hierarchy (dynamic check!) | *** casts pointers/references within an inheritance hierarchy (dynamic check!) | ||
*** requires that the type have at least one virtual function | *** requires that the type have at least one virtual function | ||
*** if check fails, nullptr for ptr* cast, throw exception for ref& cast | *** if check fails, nullptr for ptr* cast, throw exception for ref& cast | ||
− | ** reinterpret_cast | + | ** <code>reinterpret_cast<>()</code> |
*** reinterpret pointers/references as pointing to some other type (no check, no limit!) (only really safe for char*) | *** reinterpret pointers/references as pointing to some other type (no check, no limit!) (only really safe for char*) | ||
*** reinterpret pointer as an integral value | *** reinterpret pointer as an integral value | ||
− | ** const_cast | + | ** <code>const_cast<>()</code> |
*** remove constness | *** remove constness | ||
− | ** c-style cast (int) etc. | + | ** <code>(...)...</code> c-style cast (int) etc. |
*** can do all of the above, even if you don't mean to<br /> | *** can do all of the above, even if you don't mean to<br /> | ||
***<code>const Base* pBase = &obj;</code> <br /> | ***<code>const Base* pBase = &obj;</code> <br /> | ||
Line 54: | Line 55: | ||
* Using RTTI (Runtime Type Information) [https://youtu.be/g-NGBFCn3co?t=13m01s 13:01] | * Using RTTI (Runtime Type Information) [https://youtu.be/g-NGBFCn3co?t=13m01s 13:01] | ||
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
− | ** To use RTTI, include <code><typeinfo></code> and use operator <code>typeid()</code> to obtain the type id of any dynamic type | + | ** To use RTTI, #include <code><typeinfo></code> and use operator <code>typeid()</code> to obtain the type id of any dynamic type |
** Use case: check if two objects are of the same derived class: <code>if( typeid(f1) == typeid(f2) )</code> | ** Use case: check if two objects are of the same derived class: <code>if( typeid(f1) == typeid(f2) )</code> | ||
** <code>typeid()</code> returns an object of type <code>std::type_info</code> | ** <code>typeid()</code> returns an object of type <code>std::type_info</code> | ||
Line 61: | Line 62: | ||
<div class="mw-collapsible-content"> | <div class="mw-collapsible-content"> | ||
** Get the type name of an object by calling <code>typeid(obj).name()</code> | ** Get the type name of an object by calling <code>typeid(obj).name()</code> | ||
− | ** You can't use typeid in switch statements, but you can use them in map class to get similar functionality | + | ** You can't use typeid in switch statements, but you can use them in a map container class to get similar functionality |
** typeid does have an overhead, like dynamic_cast, so avoid its use in super performance critical applications | ** typeid does have an overhead, like dynamic_cast, so avoid its use in super performance critical applications | ||
</div> | </div> |
Latest revision as of 21:53, 16 October 2019
In this video Chili teaches us how to figure out what our polymorphic pointers are actually pointing to (aka "type discovery"). Just note that although we can do this, it is generally a weaksauce way to go about things. Virtual functions are 1000% more hype than type discovery bullshit. Oh yeah, we also finally see all the C++ style casts united.
Topics Covered
-
dynamic_cast<T*>
anddynamic_cast<T&>
-
const_cast
- Overview of all C++ style casts
- RTTI with
typeid()
- The
type_info
class
Video Timestamp Index
- Using
dynamic_cast<DerivedType>(pBaseType)
to determine to what derived type a pointer to a polymorphic base type is actually pointing 0:54
- A dynamic cast can be used on pointers and on references
- To cast (from and to) a pointer, use
if( DerivedClass* ptr_temp = dynamic_cast<DerivedClass*>(ptr_to_baseClassObject) )
(note 1: if the cast fails, it will return a nullptr of the DerivedClass type)
(note 2: nullptr is #defined to 0 and thus evaluates to false inside an if statement) - To cast (from and to) a reference, you can use
DerivedClass& val_temp = dynamic_cast<DerivedClass&>(baseClassObject)
(but that will throw an exception when the cast fails so it cannot be used inside an if statement) - A dynamic cast can be a costly operation (depends on the inheritance tree, nature of the cast, compiler)
- Dynamic cast only works for types that have at least one virtual function (the operation needs information from the vtable)
- Using
static_cast<>()
to convert pointers within an inheritance hierarchy 6:20
- Suppose: you have a polymorphic
DerivedClass
that has a nonvirtual member functionFoo()
- You have a pointer
BaseClass* ptr = new DerivedClass
to the polymorphic base class that you know points to the derived class (the "dynamic type") - To call the derived class' member function, you can use a static cast to DerivedClass pointer
static_cast<DerivedClass*>(ptr)->Foo();
- ... or a static cast to DerivedClass reference
static_cast<DerivedClass&>(*ptr).Foo();
- Suppose: you have a polymorphic
- Using
const_cast<>()
to remove a const from a reference or pointer (yes, you read that right) 7:27
- Example of a responsible use case: to overload a [member function that reads from/writes to an array] with a [const version of that function that can only read and returns the value as a const reference]
- You need that overloaded function if you have a const reference to the object and you want to call the member function
- Example case inside the
TileMap
class of the Snek game: the body of the overloaded getter function readsreturn const_cast<TileMap*>(this)->_At(pos);
in which you remove the const from the pointer to the self object
- Overview of different types of casts 9:30
-
static_cast<>()
- converts types (int->float etc.)
- casts pointers/references within an inheritance hierarchy (no checking of the validity of the cast!)
-
dynamic_cast<>()
- casts pointers/references within an inheritance hierarchy (dynamic check!)
- requires that the type have at least one virtual function
- if check fails, nullptr for ptr* cast, throw exception for ref& cast
-
reinterpret_cast<>()
- reinterpret pointers/references as pointing to some other type (no check, no limit!) (only really safe for char*)
- reinterpret pointer as an integral value
-
const_cast<>()
- remove constness
-
(...)...
c-style cast (int) etc.- can do all of the above, even if you don't mean to
const Base* pBase = &obj;
- ...
Derived* pDerived = (Derived*)pBase;
- casted away the constness by accident!
- can do all of the above, even if you don't mean to
-
- Using RTTI (Runtime Type Information) 13:01
- To use RTTI, #include
<typeinfo>
and use operatortypeid()
to obtain the type id of any dynamic type - Use case: check if two objects are of the same derived class:
if( typeid(f1) == typeid(f2) )
-
typeid()
returns an object of typestd::type_info
- To use RTTI, #include
- The
type_info
class 15:05
- Get the type name of an object by calling
typeid(obj).name()
- You can't use typeid in switch statements, but you can use them in a map container class to get similar functionality
- typeid does have an overhead, like dynamic_cast, so avoid its use in super performance critical applications
- Get the type name of an object by calling
- Main take-away 16:40
- You can discover a dynamic type at runtime and act on that, but it is much more elegant to avoid this and use dynamic dispatch (smart design of virtual functions that dispatch to the correct implementation) to do this for you
- Use virtual functions to their full effect. Only as a last resort, try to discover the underlying type and act on it
Source Code
Note that the code for this video is in a different branch called "casting". You will not find it in the master branch.