static_cast means it works on compile-time.
Finding the code is easy.
Error found at compile-time.
-
a = f ; // hard to find in code in case your app failed a = static_cast<int>(f);
-
char c ; int *p = (int*)&c ; *p = 5 ; // PASS at compile-time but may FAIL at run-time. int *ip = static_cast<int*>*(&c) ; // FAIL , compile-time error
-
class Base(); class Derived: private Base{}; int main() { Derived d1; Base *bp1 = (Base*)&d1 ; // Allowed at compile-time Base *bp2 = static_cast<Base*>(&d1); // Not allowed at compile-time }
-
Base *bp1 = static_cast<Base*>(&d1); //OK Base *bp2 = static_cast<Base*>(&d2); //OK Derived1 *d1p = static_cast<Derived1*>(bp2) ; //NEVER do that though compiling ok // should use dynamic_cast instead.
-
int i = 10 ; void *v = static_cast<void*>(&i); int *ip = static_cast<int*>(v);
Dynamic_cast is used for only 1 reason: to find out correct down-cast(base -> derived).
- SYNTAX:
dynamic_cast<new_type>(expression)
- NOTES:
- Need at least on virtual function in base class
- works only on polymorphic base class
- because it uses this information to decide about wrong down-cast
- If the cast is successful, dynamic_cast returns a value of new_type
- If the cast faild , and
- new_type is a pointer, it returns a null pointer of that type
- new_type is a reference type, it throws an exception.
- Need at least on virtual function in base class
Derived1 d1;
Base *bp = dynamic_cast<Base*>(&d1);
Derived2 *dp2 = dynamic_cast<Derived2*>(bp);
if (dp2 == nullptr) ... ;
try {
Derived1 &r1 = dynamic_cast<Derived1&>(d1) ;
}
catch( std::exception& e) {
...
}
- using this cast has run-time overhead, because it checks object types at run-time using RTTI.
- if we are sure that we will never cast to wrong object then we should always avoid this cast and use static_cast instead.
- NOTES
- smart pointer is a class which wraps a raw pointer, to manage the life time of the pointer
- The most fundamental job of smart pointer is to remove the chances of memory leak.
- It makes sure that the object is deleted if it is not reference any more.
- TYPES
- unique_ptr
- Allows only one owner of the underlying pointer
- shared_ptr
- Allows multiple owners of the same pointer ( Reference count is maintained )
- weak_ptr
- It is special type of shared_ptr which doesn't count the reference.
- The best use of this weak pointer when there is a cyclic dependency when we use shared_pointer.
- unique_ptr
- NOTES
- unique_ptr is a class template, provided by c++11 to prevent memory leak
- unique_ptr wraps a raw pointer in it, and de-allocates the raw pointer when unique_ptr object goes out of scope.
- similar to actual pointers we can use
->
and*
on the object of unique_ptr, because it is overloaded in unique_ptr class. - when exception comes then also it will de-allocated the memory hence no memory leak
- Not only object we can create array of objects of unique_ptr.
- OPERATIONS
- release, reset, swap, get, get_deleter
#include<iostream>
#include<memory>
using namespace std;
class Foo {
int x ;
public :
explicit Foo(int x): x{x} {}
int getX() {return x;}
~Foo() { cout << "Foo Dest" << endl ; }
}
int main() {
// Foo *f = new Foo(10;
// cout << f->getX() << endl ;
// memory leak in case forgot to call `delete`
// you don't have to call `delete`
// on pointers you created
std::unique_ptr<Foo> p(new Foo(10)) ;
cout << p->getX() << endl ;
// exception safe
std::unique_ptr<Foo> p2 = make_unique<Foo>(20);
// p1 = p2 ; // FAIL: can not copy ownership
unique ptr<Foo> p3 = std::move(p1); // PASS: moving ownership is allowed.
// now p1 is equal to nullptr
Foo* p = p3.get() ;
Foo* p4 = p3.release() ; // now p3 is nullptr.
p2.reset(p4); // the pervious pointer handled by p2 is deleted.
}
- NOTES
- can share the ownership of object
- It keep a reference count to maintain how many shared_ptr are pointing to the same object, and once last shared_ptr goes out of scope then the managed object gets deleted.
- shared_ptr is threads safe and not thread safe ? a. control block(which maintains the reference count) is thread safe b. managed object is not.
- There are 3 ways shared_ptr will destory managed object a. If the last shared_ptr goes out of scope b. If you initialize shared_ptr with some other shared_ptr c. If you reset shared_ptr
int main() {
std::shared_ptr<Foo> sp(new Foo(100)) ;
cout << sp.use_count() << endl ; // 1
// copy value
std::shared_ptr<Foo> sp1 = sp ;
cout << sp.use_count() << endl ; // 2
// reference OR pointer (not value) doesn't work
std::shared_ptr<Foo> &sp2 = sp ;
cout << sp.use_count() << endl ; // 2
}
- NOTES
- if we say unique_ptr is for unique ownership, and shared_ptr is for shared ownership, then weak_ptr is for non-ownership
- It actually reference to an object which is managed by shared_ptr
- A weak_ptr is created as a copy of shared_ptr
- We have to convert weak_ptr to shared_ptr in order to use the managed object
- It is used to remove cyclic dependency between shared_ptr
int main() {
auto sharedPtr = std::make_shared<int>(100) ;
std::weak_ptr<int> weakPtr(sharedPtr) ;
// check whether this shade pointer is existing or not
// if exists , convert to an shared_ptr to prevent the managed object from deallocation.
if (std::shared_ptr<int> sharedPtr1 = weakPtr.lock()) {
std::cout << sharedPtr1.use_count() << std::endl; // 2
} else {
std::cout << "don't get the resource" << std::endl;
}
}