In this lecture, we'll implement another example of inheritance in C++. Here's the class hierarchy we're going to implement. This is a canonical inheritance example that computer scientists use. The root of our class hierarchy or the top of our class hierarchy is a bank account class that has fields to keep track of the balance in the account and vectors for the deposits made to and the withdrawals made from that bank account. We have a constructor and a destructor, getters for those three fields, a way to make a deposit, a way to make a withdrawal, a function that prints information about the account, and a SetBalance function that we'll talk about in more detail when we're looking at the code. We have a checking account child class that inherits all that stuff from the bank account, but also stores a vector of the checks that have been cashed. It implements a constructor and a destructor, a getter for that vector of the checks, a way to cash your check, and it overrides the Print function to print information about the checking account. Finally, we have a savings account child class that inherits everything from its parent bank account class, but also has a field for an interest rate, implements a constructor and destructor, provides a mechanism to accrue interest, and also overrides the Print function. Let's go look at the code. Again, we'll start with the top of our class hierarchy. So this is our bank account class. As you can see, I have some private fields or member variables, I have a balance, I have a vector of deposit amounts, and I have a vector of withdrawal amounts. I have a constructor that starts our bank account object with an initial deposit, I have a destructor for our bank account, we have a getter for the balance in our bank account, but I don't have a setter for the balance. We don't want to let a consumer of the class directly change the balance, we want them to change the balance by either making deposits or making withdrawals. We can get a vector of the deposit amounts and we can get a vector of the withdrawal amount. We can make a deposit by providing an amounts to deposit. We can make a withdrawal by providing amount for the withdrawal, and we can print the account class and balance. This is just a utility function that I've provided so that we can get some reasonable output when we run the code. Notice I've marked the Print function as virtual, so my child classes can override the Print function to print class-specific information about the child class rather than the parent class. I do have a protected function called SetBalance, and I'll show you why I've done that soon. But a member function or a member variable that's protected is only accessible to the bank account class and child classes of the bank account class; no other class can access this SetBalance function, only bank account and child classes of bank account. In the implementation of bank account in my constructor, I'm going to call my MakeDeposit function with the initial deposit. Because as we'll see soon, I do some work in the MakeDeposit function to validate the deposit that's being made, and there's no reason to duplicate that code. Opening the bank account with a particular initial deposit is just making a deposit to the bank account. My destructor still doesn't do anything. Remember, we'll do stuff in our destructors when we get to polymorphism. I can get the balance. That's pretty straightforward. I can get to the deposits and withdrawals. Here's that MakeDeposit function. We want to make sure that the deposit is greater than zero. If it is, we'll increase balance by the amount and we'll add that amount to our deposits vector. If the amount is less than or equal to zero, we'll print a message that says deposits have to be larger than zero. Our make withdrawal function does a little more error checking because we have to check both that the amount of the withdrawal is less than or equal to the current balance in the account, and that the amount is greater than zero. If the amount were less than zero, we'd be providing a negative amount. We'll see here, we subtract the amount from balance. If we could provide a negative amount, we could call make withdrawal with the negative amounts and increase the balance in our bank account. That's obviously not the way they should work. If I have a valid amount, I deduct that amount from the balance and I push the amount onto my withdrawals vector. Because I want specific error messages based on how the amount validation failed, if the amount is greater than balance, I want to say there's not enough money. If the amount is less than zero, I want to say withdrawals have to be larger than zero. That's just providing specific error message is based on what the error actually is for the invalid amount. Printing the bank account, I'm just going to say it's a bank account and I'll print the balance. Here's that protected function that can only be called by this class and child classes of this class. When I say child classes, they don't have to be direct children, they could be further down the class hierarchy. But if you can reach bank account from wherever that class is, then they can call this protected function. As long as we haven't set a base class access specifier somewhere in the middle to be private, but we're not going to do that in this class. We're validating the amount for setting the balance because even though we trust our children, we want to make sure they're not doing anything silly. We want to make sure that setting the balance is setting it to an amount that's greater than or equal to zero. If it is, we'll just set the balance to that amount. If it's not, we provide an error message that says balance amounts have to be larger than zero. This is our base class. This is the root of our class hierarchy. Let's look at our checking account next. Remember our checking account is a child of bank account and I'm just going to keep using the public base class access specifier. We have an extra field here that we don't have in our parent class and that's a list of check amount. Now I will admit that we'd probably actually have a check object that had a checked number and the amount and who it was made out to and all those things, but I'm just keeping this example simple to let us focus on inheritance. We do have an extra field or a member variable in this child class, the vector of checks. I have a constructor, I have a destructor, and then I have some new functionality that wasn't in our base class. I want to be able to get the vector of checks. I want to be able to cash a check. I want to be able to override print. We'll see how I override print when we look at the implementation file. This is, as you'd expect, I'm just calling the parent constructor here within the child class constructor. The destructor doesn't do anything. This returns the vector of checks. Cashing a check, we validate the check amount. Here's why I'm using that protected function. What I'm doing here is I'm getting the current balance and I'm subtracting the amount that this check is for, and then I'm setting balance to that new balance, and I push the amount onto my vector of checks. Some specific error messages in case the check amount isn't valid. Here I've overridden the print function in my parent class so that I can say checking account and call get balanced. Now there is another way to give the checking account object access to the balance in bank account. I could come up to bank account, and Instead of having this balance be private, I could make it protected. If I do that here in my checking account, we'll just look at cashing a check. Instead of this, I can say balance minus equal amount. This will compile fine, and it will work properly and so on. However, there is a philosophy that says that even child classes shouldn't be allowed to manipulate directly the member variables in the parent class. As you can see in our SetBalance function, we actually do some validation here to make sure that the balance isn't getting screwed up because the BankAccount class is responsible for the data integrity contained in itself as an object. If we do this, then our parent class doesn't have that control over the data integrity of its fields or member variables. Although this works okay, it's generally better to provide protected setters in the parent class that the child classes can call to manipulate those member variables in the parent class. I'll actually show you that if I make this private again, I can no longer compile because the error message says, I can't access a private member in BankAccount. Even from within the child class, that field is private to the BankAccount subobject that's contained in my checking account object. We'll get rid of this code and go back to using our protected SetBalance function. Finally, let's look at SavingsAccount. Our SavingsAccount also is a child class of BankAccount, also with the public base class access specifier. We have an additional field for the interest rate. Our constructor looks a little different because now, it has two parameters, the initial deposit and the interest rate for the account. We have a destructor. We have a new function that lets us accrue interest, and we're overriding the print function as well. If we look at our implementation file, we can see that the SavingsAccount constructors calls our parent constructor and actually does some work in the body of the constructor. Here's an example of a child class constructor initializing the base class sub-object and then doing some additional work, specifically validating the InterestRate. If the InterestRate is valid, we set the InterestRate field to the InterestRate parameter, and if not, we provide an error message accruing interest, we get the CurrentBalance, and then we take that CurrentBalance, and we add CurrentBalance times InterestRate to it. This is our new balance, so we call that protected SetBalance function to set our balance in our BankAccount subobject. Overriding print so I can say Savings Account. Finally, here in our main function, I'm creating a BankAccount object, a CheckingAccount object, and a SavingsAccount object, and telling each of them to print itself. Then I'm depositing $20 into each account. Remember, BankAccount implements the MakeDeposit function and CheckingAccount and SavingsAccount inherit that function. We print them again. There are some class-specific things we do here. We know our CheckingAccount class exposes a CashCheck function, so we call that, and our SavingsAccount class exposes an AccrueInterest function, so we do that on our savings account, and then we print each account one more time. When I run my code, as you can see, the first three lines are calls to the print function on our BankAccount object, our CheckingAccount object, and our SavingsAccount object. They each have a different message they print, and then they print the balance in those accounts. For the next group of output, we made a deposit of $20 to each of those objects, so now they each have $20 more. Then finally, the BankAccount stated to 220, the CheckingAccount, we cashed it $20 check, and the SavingsAccount, we accrued interest. To recap, in this lecture, you've got more practice with inheritance in C++. You learned about the protected access specifier, and you also learned that it's really best practice to have child classes work through a setter to change fields in the parent class rather than accessing an inherited field or member variable directly.