- 4 kinds of casts (see last time)
dynamic_cast
vector<Book *> myBooks;
Book *bp = myBooks[0];
Comic *cp = dynamic_cast<Comic *>(bp); // If this fails, then nullptr is returned
if (cp) cout << cp->getHero();
else cout << "Not a Comic";
- Tentatively try the cast
- if successful, cp is a valid Comic ptr
- if not successful, cp becomes nullptr
- used to cast a
shared_ptr
to anothershared_ptr
object
static_pointer_cast
const_pointer_cast
dynamic_pointer_cast
void whatIsIt(shared_ptr<Book> b) {
if (dynamic_pointer_cast<Comic>(b)) cout << "Comic";
else if (dynamic_pointer_cast<Text>(b)) cout << "Text";
else cout << "Book";
}
- The code is tightly coupled with the class hierarchy, the above code would therefore be bad practice. Prefer
virtual
methods.
dynamic_cast
for references
Comic c{};
Book &b{c};
Comic &c2 = dynamic_cast<Comic &>(b);
// if successful, c2 is a valid reference to the Comic
// if not successful, a bad_cast exception is thrown
Remember from TODO: figured out which notes this example belongs to
Text t1{}, t2{};
Book *bp1{&t1};
Book *bp2{&t2};
*bp1 = *bp2;
- Resolve partial assignment by making operator= virtual
class Book {
public:
virtual Book &operator=(const Book &other);
};
class Text : public Book {
public:
Text &opeator=(const Book &other) override {
const Text &temp = dynamic_cast<const Text &>(other);
Book::operator=(other);
topic = temp.topic;
return *this;
}
};
Comic c{};
Book *bp3{&c};
*bp1 = *bp3; // This could throw an exception
template<typename T>
T main(T x, T y) {
return x < y ? x : y;
}
int x = 5, y = 7;
int result = min(x, y); // min<int>(x, y);
// type of T is automatically inferred
template<typename Func>
void foreach(AbsIter &start, AbsIter &finish, Func f) {
while (start != finish) {
f(*start);
++start;
}
}
- Instead of hard coding the type
AbsIter
, we can make it a template parameter as well
template<typename Iter, typename Func>
void foreach(Iter start, Iter finish, Func f) {
while (start != finish) {
f(*start);
++start;
}
}
void foo(int n) {
cout << n << endl;
}
int a[] = { 1, 2, 3, 4 };
foreach(a, a + 4, foo);
std::for_each
std::find
std::find_if
std::find_if_not
std::copy
std::transform
std::find
template<typename Iter, typename T>
Iter find(Iter first, Iter last, const T &val) {
// Search for val within the range [first, last)
// If val is not found, then return last
}
std::copy
template<typename InIter, typename OutIter>
OutIter copy(InIter first, InIter last, OutIter result) {
// copy one container's range [first, last) into another starting at result
// Require: result has enough space, no memory allocation will be performed
}
vector<int> v{ 1, 2, 3, 4, 5, 6, 7 };
vector<int> w(4); // Reserve space for 4 ints
copy(v.begin() + 5, v.begin() + 5, w.begin());
std::transform
template<typename InIter, typename OutIter, typename Func>
OutIter transform(InIter first, InIter last, OutIter result, Func f) {
while (first != last) {
*result = f(*first);
++first;
++result;
}
return result;
}
int add1(int n) {
return n + 1;
}
vector<int> v{ 1, 2, 3, 4, 5, 6, 7 };
vector<int> v2(v.size());
transform(v.begin(), v.end(), v2.begin(), add1);
// Now v2 = { 2, 3, 4, 5, 6, 7, 8 };
CS444 will implement this
class C {
int x;
public:
virtual void foo();
void bar();
~C();
};
C c;
sizeof(c); // atleast 12 due to ptr to foo()
- every time a class has a virtual method, objects of that class contain a ptr "virtual pointer" (vptr)
- Needed to implement dynamic dispatch
c.bar();
- Compiler finds the memory address for bar function at compile time
c.foo();
- For every class that has a virtual method, a single virtual table is created (vtable)
- vptrs point to virtual tables
virtual table, contains address of different virtual methods
"C" |
---|
addresses of foo |
addresses of destructor |
-
Object C has a vptr to the table above (inside the UML for Object C)
-
p->foo();
- Follow object to the Object, then follow the vptr to the vtable, then find the function address
- much more complex than a non virtual method
- different children of Object C would have different vptrs
- If the virtual method is no overridden in a child, then the address to
foo()
would be the same as the address in the parent
- If the virtual method is no overridden in a child, then the address to