Inheritance and Polymorphism
The course is part of this learning path
This course is designed to enhance your object-oriented programming skills by focusing on two concepts: inheritance and polymorphism. We'll cover the key concepts and then put them to practice with a couple of demo projects towards the end of the course.
- Learn about base classes and derived classes and how they are related
- Understand how different base classes can be used to control how derived classes inherit data and behaviors from their base classes
- Understand the fundamentals of polymorphism
- Learn about enumerated types in C++
- Beginner coders, new to C++
- Developers looking to upskill by adding C++ to their CV
- College students and anyone studying C++
To get the most out of this course, you should have a basic understanding of the fundamentals of C++.
In the previous lecture, we solidified our skills with inheritance and polymorphism with a relatively straightforward extension of project we had been working on throughout this section. In this lecture, you will work on a project from scratch, creating an inheritance hierarchy involving creating characters for a role playing game or RPG. Because this project is very involved, I have created a requirements and help document that you can refer to as you work on the project. This document will be made available as part of the resources for this lecture. So, here's your document right here. RPG character creation project documents. It explains what the project's about. That says that you're creating foundational classes to represent player character, that is, the entity within the game world, that the user of the game software would control. A role playing game or RPG, if you're not familiar, is a game in which you take on the persona sometimes called the avatar of an in- game character and by character, we don't mean like text character, like the chart, data type. What we're talking about here is a character living in the game world.
Like a toon, a person, an individual, an entity of some sort that you're controlling. So, this character will go on quests, fight enemies, look for gold, crawl through dungeons or any number of tasks, depending on the game. I have a UML diagram here for the player base class, you'll notice that it is italicized. This was not by accident. This means that it is an abstract class and I also have a method italicized down here attack. So, that indicates that this is also put the equal zero is a little hint there, that's a little c++ specific. You wouldn't normally see this in the UML class diagram, but it's okay to add little bits and pieces here and there if it helps people understand what's going on. Did you notice that the class has name, race, hit points and magic points And you'll notice that there is a single constructor that takes all four things here. You'll notice race seems to be a different type, we'll have to look at that. It's not a type that we're familiar with. It's not built in.
So, it's got to be a class or an enum or something, right? So, getName, getRace, whatRace. Now, that's going to be a little different. Get hit points, get magic points. And then you have the centers for the name, race, hit points, magic points and then an attack. So, other than the attack and the what race, which seems to be different from get race, this seems pretty straightforward. So, first you'll create the RPG project Visual Studio project. Then you have to create a base class player based on the UML above, which will contain the name, race, hit points and magic points for the player. It should provide a constructor that takes four parameters corresponding to all four of the fields. It should also be possible to change the data once you have set them. So, you need centers and also you should be able to get those four parameters. So, you need Kidder's as well. So, the fields are common to all player objects, no matter what the player type is.
So, player will be an abstract class, meaning it has to have at least one pure virtual function. And that happens to be the attack methods. So, that's the virtual function or virtual method here. Attack, there returns a string and I'm saying it's a pure virtual method. So, the derived classes have to implement the attack methods. That means we'll have derived classes. Note that in the document HP stands for Hit points. An MP stands for magic points. That's pretty well known in the RPG community and gaming community is that HP has hit points- how much damage can entity take and MP is how much magic does the entity have? That usually recovers hit points or health points, can recover over time or when the player sleeps or eats or things like that. So, that's either health points or hit points and then MP is magic. So, or mana points is another term, you'll hear that has to do with how much magic do they wield? All right, so, what are the derived classes? So, in addition to the player class, you have to implement three derived classes- warrior, priest and mage. So, the warriors will have 200 HP and zero MP- so they don't really wield magic in RPGs usually and their attack method, which remember in the player class is virtual, has to have an implementation for each of these derived classes.
So, Warriors attack method returns the string, "I will destroy you with my sword fell demon". Priests will have 100 HP or hit points 200 magic points. So, they have a lot of magic. Their attack method returns the string, "I will assault you with Holy wrath" And then magi or mages will have 150 HP. So, a little bit stronger than the priest despite his ability to take damage and a little less powerful as far as their magic points ability, their attack method for the magic will return, "I will crush you with the power of my arcade missiles". So, because most of their functionality is provided by the player class, player can have both specification and implementation files. So, that's important. The player class will have a dot H file for specifications and implementation files. However, the implementations of these derived classes are remarkably simple. So, that means you don't really need full separate implementation files. What I'd like you to do is just create dot H files and you can actually inside those implementations that will be inside of the header file, you can actually put the code for the methods that are required for warrior, priest and mage. So, constructors attack method, not really much else, right? So, you could put the empty body of the constructor for each derived class with a simple initialization list calling on the player constructor- that's probably the easiest way to pass through the other data. So, like the hit points and all that other stuff but you do know, you should note that constructors for each of these don't require the user to pass in these two, right?
So, the HP and the MP of the player requires it all four of them. The constructors for these three will really only need the name and the race. So, why is that? Why doesn't it need hit points and magic points? Well it needs it and that you will pass these values, just literal values directly to the constructor of the player class. The only two that the actual warrior, priest and mage will take in their constructors, it will be the name and the race. So, that's pretty straightforward. Now what's the race? It's not a class. It's an enum. So we covered enums in this section so that you would be able to show off your amazing talents with enums. So, it seems like we know what to do for the name which is straightforward and even the HP and MP during the construction of the derived classes. But what do we do about the character's race? The race will be represented by an enum which will have the following values or there are five possible values- human, elf, dwarf, orc and troll. So, the enum maybe coded in the player dot H. file but outside of the player class itself. Okay, so you can put it in player dot H, but you don't put it inside of the player class definition. You can put it outside as if it were just like a global variable sitting there. This one, sure all the derived classes have direct access to it. You should provide an additional whatRace method. So, that is the method that will return a string representation of the internal race of the player that's handled by the player class, not the subclass, its not the derived classes. It's different from getRace. getRace is just a standard getter like there's a center- it will return the actual enum type for the race, right? So, it will actually return, you know it's really returning value zero for human, one for elf, two for dwarf, three for orc, four for troll. But whatRace should internally handle being able to actually return a string that says human as a string or elf dwarf or troll. You don't have to capitalize those. Those can be lower case, they could be sentence case, whatever you want. Now, what should the main function do? So, this is going to be kind of an interactive program where the users get to decide what kind of characters they're creating for the game. So, in main you should allow the user to create different kinds of player objects, allowing them to select which of the derived classes they want. So, basically you word it as requesting what the this particular player's profession should be. So, from a simple menu, so you can say like one, if you put one, I will let you select human to, or I'm sorry, one means a warrior, two means a priest, three means a mage or whatever you want like that, just a simple menu. You should also let the user select the race of the character and respond accordingly. Store as many players as the user wants into a vector of player pointers you're going to be using because of polymorphism. If it's a vector of player pointers, that means it can point to any of the different player kinds, right? So, it can point to a warrior, it can point to a priest, it can point to a mage. And if you extend it later later it can point to any of the new professions or we would call them player classes in the game. But that's confusing because we use the word class for other things. So, these professions that the players have, if you decide you want a barbarian profession or a thief profession or a rogue profession or something like that, then you can add it later on. It's extendible and you better bet some of the actual games that exist like, you've got Lord of the Rings online and I'm going to age myself with this but like World of Warcraft and you've got the Defense of the Ancients and League of Legends and all these different games. They're constantly adding stuff and updating stuff. You better bet they are using and taking advantage of inheritance and polymorphism. Now, they have the added complexities of graphics and many, many different more details and skills and things to keep track of but if you're familiar with any of those games, they're very complex and that's why they have large teams of software developers. Some might be responsible for managing different spells and graphics and some might be responsible for managing the different professions or maybe they've got an entire team working on just warrior stuff and then one team working on priest stuff. So, they're very, very complicated.
So, we're doing a lot more of a simple situation but this is not a trivial project. So, the user interaction is done in the file, main. You can think of this as a separate problem from creation of the Player base class and derived classes. This should help you from feeling too overwhelmed. So, I always try to break it up into different pieces. Think about what objects you need in main. You can use appropriate functional decomposition. So, you can create different functions to make main a little cleaner itself and then you will use the different classes that we will create throughout this project. So, when you iterate through the pointers in the vector, they should each print out something like "I'm a, and you can forget about the grammar problem here because it's going to say something like I'm a elf instead of I'm an elf. You don't have to worry about that. But I will say, "I'm an elf" or "I'm a elf and my attack is" and then it prints out the attack. So, that would be the attack that you implement up here.
So, when you implement that pure virtual method, "I will destroy you with my sword foul demon!" or something like that, that is the attack that it will say. So, I will say "I'm an elf or I'm a elf and my attack is. I will destroy you with my sword foul demon!". So, take care of that. And obviously, as another reminder, make sure that you manage the memory correctly because you need to delete anything that you dynamically allocated. And also, since you're using a vector, once you have deleted each of the pointers that's inside the vector, you also want to call clear on the vector. So, maybe you break that out into a function inside of main. So, this is a significant challenge. It involves all three of the primary principles of Object-Oriented Programming. It also adds some usage of enum into the mix which you just learned as well, like the Tic-Tac-Toe project from earlier in the course. This is the type of project that I might assign my in-person students that would maybe take them a week or so to work on. So, take your time. Think it through.
Take the time to design it and sketch it out, make drawings. Think about what you're going to do and then construct the classes and main file with user interaction. Rushing through everything is not how you become a better developer. Just getting that certificate at the end or some sort of evidence that you've finished a course of some sort is not what makes you a better developer. As you become better, your development speed will also improve. So, don't worry so much about that. But while you're learning, take the time to really understand the material and to savor the learning process. Again, it's not about getting some piece of paper that says you did something. I tell my students whether they're at junior or community colleges or universities. I say that piece of paper they give you doesn't say anything. It might get you through the door but it will not keep you in the building. Your knowledge is going to be keeping you in the building, your knowledge and your skills. So, pay attention to that. I've got the RPG Project sitting here. You'll notice that if I run it, Start Without Debugging, it should look something like this with the interaction. So, let's say we want to create a warrior. Let's say, what do you want to name it? Maybe, Bob and maybe a human. Maybe, we'll create now a Mage. So, I select three. What do you want to name your character? Let's say Alice. And it's a Troll. Let's get a priest, number two. Name the character John and he's a human. And let's say that's all we want. So, we can type zero and finish creating players and it should print out I'm a Human and my attack is : I will destroy you with my sword foul demon!. I'm a Troll and my attack is: I will crush you with the part of my arcane missiles! because that was a troll mage. The human was a warrior and then this other human is a priest. You can also print out their names if you wanted to. I didn't in this case but that's fairly trivial to add printing out the names. So, hopefully that helps. So, when you're ready, pause the video. Come back when you're done and we'll look at my implementation. This project was pretty challenging. How did it go for? Don't feel bad if you felt a bit overwhelmed. Let's work on it together. So, let's create the RPG project. So, we'll close this for now and we will create the RPG project here. Create a new project. We'll call it just RPG project or role playing game and hit Create. We should structure this carefully and we need a few different files. So, let's just be careful here. We need a main.cpp. That's for sure.
So, let's get that ready. Journey of a 1000 steps begins with one step. So, we got main.cpp. And remember, I said you don't need separate class files for the Warrior, Priest at major rather implementation files but for player, we do need a .h and a .cpp. So, let's do Player.h. Just start from top down here and then do Player.cpp to get it ready. And this is where the majority of the code for the classes will go, as in Player.cpp. Now for the header files, we need also, I'm calling them header files now because these actually do contain it. They will contain an implementation. So, that can be a little bit confusing. So, Warrior.h and we will do also, who else? Priest.h. Make sure we spell it right. P-R-I-E-S-T and of course, the mage.h, Pretty common professions in an RPG. So, we will fill out the main skeleton. So, let's at least get the skeleton started here. I will, of course, be filling in more stuff later. We'll include a lot of these other files as well. So, we've got player.h. Organise that. ifndef PLAYER_H. We will define PLAYER_H, endif, and we'll be using string in here. So, we need string using namespace std;. And we have our enum race too. Just so we don't forget it, we'll just do it now. (HUMAN, ELF, DWARF, ORC, and TROLL);. And we have class Player. Now, what do we need in class players? So, it's got a lot of public stuff and a lot of private data as well. So, let's see what we've got here. So, we have a string name;, a Race race;, int hitPoints;. You can call it healthPoints; if you'd like and magicPoints;. There we go. And then for public, we have quite a few methods to implement. Player takes. This is the constructor. So, (string name, Race race, int hitPoints, and magicPoints);. And this is the base class so we're not doing any extensions. We don't need any extra code at the top. We have getName(). You have Race getRace(), string whatRace(). That's kind of an extra special one that we're implementing but it also does not change anything. getHitPoints() const;. And to getMagicPoints() const;. We also have some setters. So, if you want to keep these little organizer, got the constructor, some getters. And then we have some centers. So, we've got setName which takes a string. It cannot be const because it makes changes and neither can any of these other setters. And we've also got setHitPoints (int hitPoints);. hitPoints. setMagicPoints which will take magicPoints. And we of course, have the attack method, which is a pure virtual method virtual string attack() and then it is const because it doesn't make any internal changes. Now, if you designed it so that instead of just returning a string, it also causes damage or you lose magic or whatever, then it could not be const because it would have to manipulate internal data but for our purposes, all it does is print some star, well, return a string that will be printed to the console or displayed in some other way. There we go. There's a pure virtual method. I don't need it to grab that one. I just need to grab the other ones and we will copy that. And I'm going to go over into Player.cpp. I need to include the "Player.h" file and I'm going to paste these. There's quite a bit we need to do now.
So, I guess for the getters, I can separate that out a little bit just make it look a little bit prettier because there's a lot of code in here and no biggie here. Just making a little more organized. So, give them all bodies like I would normally do. And same thing here. Enter. Same thing here. Enter. Scroll up. Same thing here. Same thing for HitPoints. Same thing for getMagicPoints. And we go down to the setters. Give those bodies. So, this is pretty substantial. This is a pretty decent sized class. It's not very simple. It doesn't have just a couple of tiny parts. So, we've got quite a few parts moving here. Now going back up here. Remember, start on it, work on it and you will finish it eventually. There you go. Player ;; or :: rather to make the scope resolution operator and then put it in between the return types on each of these and the identifiers of the methods there and there and there. setRace, setHit Points, setMagicPoints. So far so good. Now, let's actually start filling in some code. That was a lot of work for no real instructions. So, this name is name. This race will be set to race; and this hitPoints = hitPoints;. This magicPoints = magicPoints. And of course, we have some of these. return name;. That's pretty simple. return race;. We'll come back to whatRace in just a second. return hitPoints;. Forget magicPoints. return magicPoints;. You notice there's no additional like set whatRace or anything like that because this will determine the string based on the race that we already have as an internal piece of data. For the setters, this->name = name;. this->race = race;. this->hitPoints = hitPoints; and this->magicPoints = magicPoints;. Now, let's come back to the whiteRace method which is a little bit more complicated. So, I'm going to have a string result. Just to have it set to something, I'll give it an empty string and return result. Now, how do we set it? So, if the race is equal to HUMAN, we'll set the result equal to Human. I decided on sentence case. You can make it all lower case, all uppercase. Doesn't really matter. if the race is "ELF", then I set the result to "ELF";. And while you're typing these, be very careful about the == versus the =. This is comparison. This is assignment. So don't get them switched up. Let's also be consistent with that. See? It's easy to goof. So, be careful. DWARF result is "Dwarf". else if (race == ORC), result is "Orc". else if (race == TROLL) the result = "Troll". Make sure I got all my ;. Everything else looks pretty decent. I think we're good. So, that's the Player class. That's quite a bit. And if at any point you need to pause and rewind and go back and see what I did, that's totally fine because this is a very involved project. This is not simple. I can actually close that if I'm happy with it.
Same thing with the Player.h just to get stuff out of the way. And I'm going to put Warrior.h, Priest.h and Mage.h in order here. And remember, we only have .h files. So, they're header files. I'm reluctant to just call them Specification Files because we will put implementation in here. So, define WARRIOR.H endif. And if you decided you wanted to have a separate .cpp file, that's fine too. Now, let's include Player.h because we need to inherit from it. class Warrior : public Player. It has a public section. There's no additional private section because there's no additional data for this Warrior. Now, in real life, maybe you would add something else but in this case, we don't need it. So, (string name, and Race race). And we could try doing the setting inside of a body for the Warrior but the body is actually going to be empty for this constructor because we're going to make Player do the work for us.
We have (name, race) but remember, Player constructor takes four parameters. So, we're passing a name and race as arguments. But remember, for the hitPoints or the healthPoints, the warrior has 200 of those and zero magicPoints. That's what we wanted. And now I'll just give it an empty body and then string attack. We have to implement this because we are a derived class and we are not an abstract class ourselves and we will return, "I will destroy you with my sword, foul demon!";. Looks pretty good to me. So, it's complaining about the e noon type. It says, "Hey, there's a new way to do this", but we know what we're doing. Rolled hack at this. It's fine. So, we've got the string attack here. This should be okay. Did Player.h already includes string? So, that should be okay for this. It's not coloring it. It's kind of irritating but let's go to Priest.h now. So, for Priest.h. ifndef PRIEST_H. define PRIEST_H, endif. class Priest. We need to include Player, don't we? Player.h. The class Priest : public Player and then public and we have Priest (string name, Race race). And of course, inheriting from Player and using the constructor of Player. The Priest, of course, has 100 hitPoints but 200 magicPoints. So, that's what that's for. string attack() const. return "I will assault you with Holy Wrath!". So far so good. Looks pretty good to me. Now, what about Mage.h? So, what do our Mage.h look like? ifndef MAGE_H, define MAGE_H, endif. In this, class Mage : public Player. The constructor is in the public section. Mage (string name, Race race). You could even do a little bit of copying and pasting if you want. Player (name, race, 150, 150) and we have string attack() const.
There we go. return "I will crush you with the power of my arcane missiles!";. There we go. Looking good. Now is a busy part. So, here's another big one. We're going to close that and close that. We're back at main. So, main includes a whole lot of stuff. So, we'll have include of string and we need to include vector, Player.h Warrior.h, Priest.h and Mage.h. Quite a bit going on here, isn't it?
Now, to make my life a little bit easier, I actually broke this up into some function and I will show you what functions I used. I purposely did not tell you because I wanted you to have more struggle coming up with something because that can help you grow better. So, I have two print menus. I have MainMenu and the RaceMenu. And it's very unlikely your implementation will look exactly like mine and that's totally fine. So, I'm going to have createPlayer. If you pass it in a playerName, a typeNum and a raceNum and we'll see what that looks like in just a second. That will have to do with the menu. doCleanup. That will take the (vector<Player*> < Player *>playerList); and it will perform our little clean up for us. We will get the race based on the raceNum chosen from the menu and printAll. So, this takes the (vector<Player* < Player*playerList); There we go. Pretty good. I will implement these and then come back and fill in main. These are not member functions but they do need bodies. Because currently, they ain't got no body. Song for those interested: Ain't got nobody. So now, printMainMenu is going to look something like this. So, we'll print out a little bit of extra space at the beginning and which of the following would you like? Which of the following would you like? And this is the one that's going to allow them to create a warrior, priest, mage or exit the program or will finish the program and then it will exit shortly. So, we will do this "\t1 - Create a Warrior" << endl;. Now, you can copy and paste if you'd like to. I'm going to type it out. So, it gives you a little bit more time. "\t2 - Create a Priest" << endl;. I know I type fast and I sometimes get into quick talking mode so it's totally fine to pause me if you have to. That's one of the beauties of doing an online courses, you can pause your instructors. So, I've done it millions of times even with slow talking instructors because sometimes I needed to write something down or catch up or do something else or have them repeat something. There we go. And also, finally, we're going to put "\t0 - finish creating player characters"<< endl;. That would be the primary menu that we're going to deal with. So, we'll get back to that when we get to main. Print the RaceMenu. The RaceMenu is going to say now, which race do you want? So, we have \t1, this will be a separate question, obviously. We're going to be taking integers in this input so you can probably see we're doing that. "\t2 - Elf" << endl;. Now, you might have chosen to have them actually type out the name. I'm just trying to make it a little easier on them. So, this is a little bit closer to a text based adventure game in some ways, looks like the beginning of one. So, Orc and "\t5 - Troll" << endl;. You could use the main and apply and to manipulate it. No pun intended to your whim. Now, what about createPlayer? What does this look like? Well, we have to get the race. So, since it calls getRace, let's actually implement getRace real quick just to make it work really well. So, here we're going to have a race and we're ultimately going to return the race. Now, if the raceNum that's passed in, remember our menu. Also remember to use ==. If the number that's passed in, it will be passed in from the menu, is a 1, we create a human. 2 is an Elf, 3 is a Dwarf rather, 4 is an Orc. 5 is a Troll. So, race = HUMAN. Also for raceNum, you could use a switch statement here or something completely different. Maybe you came up with something very different. That's okay. So, we've got, else if( raceNum == 3), that's a dwarf. else if (raceNum == 4) and that means that the race = ORC. And then, I just decided this time I was going to just put an else just to have a little bit of variety and that will be the troll. And if you want to add any races to this, you would have to adjust this as well. So, let's see here. That was getRace. Let's go back up to createPlayer. So, with the createPlayer, it makes use of getRace. So, that's why I went to that trouble Race playerRace = getRace(raceNum); right there. So, that's why I broke it up into a separate function so to make it easier. Player* tempPlayer = nullptr; to start with, and we will ultimately return teamPlayer; but we will hopefully, get an actual instance here. So, if(typeNum == 1) and if the (typeNum == 2). And also, if the (typeNum == 3). Remember, these are based on the menu. So, what did 1 mean? 1 meant a Warrior and then 2 was the Priest, 3 was the Mage. So, that's the type. So, tempPlayer, since this is a player*, it can point to any of these types. We will allocate them dynamically here. Warrior(name, playersRace);. Very good. So, it looks like we have playerName here. And we can have tempPlayer = new Priest(playerName, playerRace); and then right here, tempPlayer = new Mage(playerName, playeRace);. Hopefully that looks pretty straightforward. It's sophisticated though, so don't feel bad if you didn't even consider any of this. That's totally fine. You have to develop your problem solving skills as you go along and that's why it's good to watch someone else do it. Maybe you think, hey, that's a really cool way he did it. Maybe, I like his better than the way I did it or maybe you like yours better than the way I did it. So, it's totally, totally cool. Now, so we've got the menus. We've got createPlayer. We need to doCleanup. So, what does doCleanup look like? Well, we could use a range based for loop. That's totally fine. Or we could use a regular for loop. So, I'm going to say for every player * in the playerList of pointers which is a vector, I will delete player. Not really much use in setting them all to null pointer although you could but we're immediately going to call playerlist.clear();. So, that will actually reduce the size of the vector all the way down to zero and that's that with that. So, what else do we need? So, printAll. printAll. Remember the format we chose here? We're actually going to do the same thing. for (Player* player : playerList). We're going to do the same thing with the enhanced or range based for. But now, remember it's "I'm a", and then we say whatRace(). That's the string, like I'm an Elf, I'm a human, whatever. <<" and my attack is : ". And then we can do player - >attack() << endl;. Looks pretty straightforward to me. Quite a good amount of coating here. Let's see what we've got in main. So, we're going to scroll up to main and then that's where the action is actually going to take place, where we're going to set up some stuff. So, choice;, int raceNum;, string playerName; which we will take from the keyboard. Now, there's a couple of things I think you probably or may have missed and I wouldn't be surprised if you did because it's easy to miss. We're going to printMainMenu(); and that's obviously saying which of the following do you want? And then they enter an integer. So, this is the priming read essentially. You could use a different kind of loop like a do while or something. Remember, when you take in integers, we're going to be taking in some strings because they'll be typing in a name. We need to consume the newline character. So, this is one thing I am willing to bet cause some people some trouble. Right? Because that was a little while back we dealt with that, so maybe, it's not quite fresh in your memory. But that's why this is much more challenging, right? So, here's the while loop. Eventually, we'll have a next round and then that will involve us printing the main menu again. cin into the choice and then cin.get(), consuming the newline character again. And when they enter the choice, if they choose zero, that means that this loop will break. And then once it breaks, we will actually printAll(). We'll come back to the loop in just a second. But we will printAll(), so that will print all the data. And then once we're done printing, we can call doCleanup() on playerList. See how nice and compact that is? It's better than having all the code just strewn around in main, right? And then just to show that we're done I'm putting Program done at the end. That wasn't in the requirements. I'm just putting it there. All right, back to our loop here. What do we do exactly? So, by the time they get to the top of this loop, whether it was from the priming read or from another round, we have a couple of things we need to do. We need to ask the user, "What would you like to name your character?" Right? And remember getline. This one, we do not have to consume the newline because this will consume the newline character automatically. Then we print RaceMenu and then they select their race choice and then we consume the newline character. And what do we do here? So, Player* tempPlayer equals, I'm going to call createPlayer(). We're passing in the playerName because it needs the name. It needs the choice, that's the actual type num. So, that's the choice. And then the race number. Remember, it will then call getRace based on the number and it will figure all that out for us. So, we've made it nice and organized. We have functions calling other functions. With the organization, this may seem like I've overcomplicated it, but I think this looks a little cleaner than stuffing everything in main. I don't just think, I know. Trying to be cordial. Okay so, playerList.push_back(tempPlayer) and there you go. So, I'm kind of reusing this. I actually could put the declaration outside of the loop. That might be a little bit better, but it doesn't really matter. We've got the tempPlayer set to createPlayer. createPlayer creates locally because it's the one that actually calls new. Right? On each of these on whichever one has been chosen and then returns the pointer to it. So, it takes care of the new wing of it. And then, once this loop is over and we've printed all the data out, we do clean up. What does doCleanup() do? It goes through, and for everything that was added in the vector, for every element that lives in the vector, which are player pointers, it deletes them. So, we are taking care of returning the memory responsibly. Then we empty the vector to make sure it's totally taken care of and we are stellar. So, that was a lot of coding. So, let's run this and see what we get. Start with our debugging. We will run the program. Which do I want to create? Let's create a Warrior. We'll name the Warrior Bob and it will be an Elf. What else do I want to create here? We'll create a Mage and we'll name the Mage Alice and Alice is an Orc. Alice the orc mage. So, what else do we have? We'll make a priest. We'll name the priest Robert and the priest is human and maybe another Warrior. We'll name this Warrior Dave. Dave is a Dwarf. And let's add one more Mage. Three. And would you like to name your character? What do we want to name it? Let's say, Jeff. And Jeff is also a Dwarf. Okay. And now, I'm finished. Now, watch what happens. So, it goes through, prints out all the different items we have in our vector and we don't see it being cleared, but it is being emptied. All the pointers are deleted and we're done. So, I'm an Elf. All right, I'm a Elf and my attack is I will destroy you with my sword. So, we know this one is a Warrior. That one is a Warrior but it's a dwarf Warrior instead of an Elf. The Orc arcane missile. That's an Orc Mage. And then this is a Dwarf Mage, right down here. And then Holy Wrath, there's only one priest and it's a human priest. Right? So, we didn't print out the names but we could. We could say my name is I'm Elf and my attack is, that's fine. You could test that if you'd like to but I think this is a pretty good proof of concept. So, that was quite a bit so nice work. Again, it's highly doubtful you came up with the same solution I did. Especially for the part with the user interaction. It's possible that you had a really hard time with this project because it was pretty challenging. But it's more realistic and at least closer to something you might create at an industry than some of the extra small projects that we've done in this course. So, it's important for me to occasionally throw in a bigger project for you to really stretch your skills. If you were able to complete this project on your own, then a huge congratulations to you. This wasn't easy. But if you weren't able to complete it, I'm happy that you tried and I'm proud of you for whatever you did get done. Whatever part you got done, that is great. That means you've learned something, right? If you felt totally lost, that's okay too. Maybe you have to review some sections and come back later and it might not seem so daunting. This was a very, very big project. I found that during my career that many times I learned more from times when I failed trying to complete something the first time around than when I easily succeed. So messing up, forgetting parts, not doing as well as you'd hoped is all part of the experience of becoming a software engineer. This project was a doozy. In the next lecture, we will wrap up this section. I will see you there.
John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.