Week 15 Day 1 - Multiple Inheritance, Virtual Inheritance, and Smart Pointers

Channel:
Subscribers:
2,640
Published on ● Video Link: https://www.youtube.com/watch?v=13-lB95Pdwk



Duration: 1:40:15
113 views
1


We hit three big topics today:


1) Multiple inheritance. A class or struct (special note: structs inherit public by default, whereas classes inherit private by default) can not only inherit from one class that it gets everything within in copy/pasted into, but multiple classes. The syntax looks like this:
class A : public B, public C {
///Everything from B and C is invisibly copy/pasted in here

};


This causes complications if both B and C have a member with the same name. For example, if both B and C have an int x, then A will have two int x's as member variables! So you can't just say "A bob; bob.x = 7;" any more - it will be ambiguous whether we want the x from B or from C. So we have to use the scope resolution operator (::) to pick which x we mean: "bob.B::x = 7 or bob.C::x = 7".


In general, though, I'd argue it's probably bad design to do something like this, but it's there for you if you need it.


2) Virtual Inheritance (Diamond Inheritance). A bigger problem happens if both B and C inherit from D. Now everything in D is copied into B, and everything from D is copied into C, and then BOTH OF THEM get copied into A. So whereas you might expect that if D had a member variable named y, you'd have one y in A, you actually get two! And you have to refer to them as B::y and C::y. This is usually, again, not what you want.


So there's something called virtual inheritance that solves that problem, and it looks like this:
class D {
int y;

};



class C : virtual public D {
};


class B : virtual public D {
};


class A : public B, public C {
//Now A has only one y from D, instead of two!

};


This is usually what you want.


Also, as I talk about in the lecture, based on my experience trying to engineer large class hierarchies, it can quickly turn into a nightmare, with surprise gotchas, hard to maintain code, code that runs that you don't even know where it is, and so forth. They're essentially (mis)using the type system to do their logic for them.


In my opinion, short, readable, understandable code is always preferable. If you gave me the choice between writing an if statement and writing 151 different classes, 151^2 functions to mediate connections between those classes, 18 interfaces for the classes to do multiple inheritance from and so forth, well, give me the if statement.


Inheritance is a really good tool in some circumstances (like I showed in the hash_table assignment how I used it to make clean looking code), but there's a tendency in some groups to use it in ways that makes code unreadable and unmaintainable. And the worst part is they claim it makes their code more readable and maintainable. C'est la vie.


3) Raw pointers are things like "int *" - the basic pointers that come with the language. They're fine if they're just pointing at things and you can be confident those things aren't going to get deleted out from under your nose. Pointers are also mostly okay if you are doing RAII properly - in other words doing a new in a constructor and a delete in a destructor. But when you're in main and new-ing things, and that pointer is getting passed around to function that don't know if they should or should not be deleting the pointer when they're done with it, then things get really tricky.


*In general*, it's probably a good idea to never call new at all these days. It's too low level and too easy to make mistakes. Rather than calling new, you can do one of two things:
1) Use a STL data structure. Let a vector manage your memory for you, or some other data structure based on your program's needs.
2) Use a smart pointer. A smart pointer comes in a few different flavors, but we talked about the two main ones today:
A) Shared Pointers, which can be copied around to different functions, saved in vectors, etc., and what's neat about them is that they track how many copies of them exist, and when the last one goes away, then it calls the destructor on the object it's managing.
You can make one with new, but I prefer using make_shared. See the example in the video, I can't type angle brackets here.


No new needed, no delete needed. Memory allocation and deallocation is now invisible to you and more or less foolproof.


B) Unique Pointers, which cannot be passed by value to a function or otherwise duplicated (i.e. no copy constructor on them exists), which is useful if you want to enforce only a single owner exists. A shared_ptr might not get deallocated if you accidentally stick a copy into a vector somewhere and forget about it. If you need to transfer ownership to a function or something, you can do so with std::move(). Example is in the video.







Tags:
csci 41
data structures
smart pointers
multiple inheritance
virtual inheritance



Other Statistics

Counter-Strike: Source Statistics For Bill Kerney

There are 113 views in 1 video for Counter-Strike: Source. There's close to an hours worth of content for Counter-Strike: Source published on his channel, making up less than 0.18% of the total overall content on Bill Kerney's YouTube channel.