Welcome back to the section 16 challenge solution.
This is part one of the solution to this challenge.
This was a very, very challenging exercise.
I hope you're able to finish this, and I hope your solution worked.
As usual, your solution could be different from mine, very likely it is.
So let's get started.
First thing we want to do is we want to make this account class and abstract class.
And we know that we can do that with the pure virtual functions.
So that's what I'm going to do right here.
I'm coming down right here to my deposit withdrawal
functions. And I'm going to say virtual,
and I'm going to make it pure virtual, right there.
My class is now abstract. I'm going to do for both of them.
And again, I've got a pure virtual function here as well.
Okay. So that was easy, right. That's cool.
Eventually, we'll get rid of this guy, and we'll replace it with the printable
interface, but we'll leave that for later.
So we've got those two pure virtual functions.
So if I try to create an account object, I won't be able to any longer.
So let's take a look at the account implementation file
and see if there's anything in here we need to change. Our constructors are fine.
You might be thinking why do I need a constructor if I'm never going to create these things.
Well, you are, right. You are through the derived classes.
Remember, the constructor calling up the chain. So it's important that we have constructors here.
There's our deposit, and there's a withdrawal.
Now these are pure virtual functions, can we implement them?
Absolutely, we can. In this case, it makes sense.
We've got a really simple deposit, really simple withdrawal.
In the case of the examples I gave you with the shape and you know how do you draw a shape in the
in the base class, you really can't. But here, we can say that deposit and withdraw,
this is the simplest case.
And I'd argue that we would leave this in here. Now there's one little thing that's missing,
that's our virtual destructor. That's really important. So let's do that.
We'll say virtual. The destructor is the name of the class with utility in front,
and we could implement this thing or we could just simply say equals default.
And that's good enough. That tells the compiler hey just give me
a default for destructor in this case, and that's fine.
I don't need any special behavior in the destructor, so that would be just fine.
All right. So now what are we going to do next.
Let's look at one of the derived classes. So let's start with checking.
So let's look at the header file for checking account.
You see here it's derived from account, that's fine.
So remember, I've got two pure virtual functions in my parent that I need to implement here.
So I'm implementing one of them right here. So
I want to make sure that this is virtual, and i want to be sure that I'm overwriting this
that way the compiler is going to help me out if I rename something correctly or if I change
one of the parameters or something.
Now what about this guy, this deposit method.
We want to implement this guy in here ourselves as well.
So we'll just say virtual
bool,
and this is the deposit method.
So we're going to implement this ourselves, and it expects to double.
And again,
I'm overwriting here. So I want to be sure that the compiler is helping me.
Okay. Perfect. What else do I need here, you guessed it.
I need a virtual destructor for a checking account.
And I'm going to let the compiler give me a default version,
which is fine. Okay. Now
this guy wasn't here before, so we're definitely going to have to implement that in the
cpp file.
So let's go into our cpp file for checking account.
And here's our withdrawal. We're calling our parents withdrawal, which is fine.
Here's our constructor that looks good as well.
Again, these output operators will get rid of them later.
But we need to implement this guy,
our deposit. And it's going to be a checking account deposit.
That's the class we're in.
We don't need the override here.
We already have it in our header file.
And we do need a name for this parameter.
So what do we do here. Well,
we're simply going to return and call our accounts
deposit method and pass in the double.
That's it, right. I have nothing special to do here so I'm just calling my parent.
So that takes care of checking account.
Now let's go down to savings account, and we did put the destructor in there right. Yes, we did.
So let's go to savings account,
same idea here. We've got
a virtual function here that we're going to override,
that's my deposit.
And we'll say override right here, and then we've got the withdrawal. So I also want to
take care of that one here. So I'll say bool. We'll call this withdraw,
and it expects that double amount.
And again, we're overriding.
This is one we'll have to implement because we were not implementing it before.
And then we also need a
virtual destructor here,
and the name of the class is savings account,
let the compiler give us one by default.
Okay. Now this withdraw method is the one we have to write.
So let's go to our implementation. So I'm opening up savings cpp.
My constructor looks fine. My deposit looks fine.
We'll get rid of the insertion operator later. But
let's implement this method here.
So this is bool for a savings account.
And all we are doing here -- again,
we don't need the override here only in the declaration.
And what we're doing here is we're just simply returning our parent, which is the account,
and we're asking it to withdraw that amount.
That's it.
And then the last one we've got is the trust account. So let me go to its header file.
And let's see what we've got here,
we already have deposit and withdraw. That's good. We've already implemented them.
So let's just be sure that we implemented them correctly. And we didn't just
that we are overriding them and not redefining them.
So we'll put override over here
as well as on the withdrawal.
Okay. That's it.
Let's take a look at the cpp file.
All that looks good.
And the last thing we need here is our destructor.
So we'll create a virtual destructor for our trust account.
And let the compiler give us one.
All right. So that's the first step. So now let's go to our main.
And you'll notice I commented out all the driver code that I provided for you. So I just want to start
really fresh here.
And the first thing I want to do is let's just do this right here. Let's just create an account
and call it Joe, real simple. Let's build and run.
And we have an error.
I forgot a semicolon there, so let's try that again.
And I spelled the virtual wrong.
Okay. Let's get all those errors out of the way.
There's account Joe, finally.
So now we have a clean compile, and it says variable Joe
is abstract. You can't abstract. I can't do this. So this is good,
right. This is a real good thing. We now know that our account class is abstract,
we can't create any.
So let's comment that out, and let's create a checking account.
Remember, this is not using a base class pointer, this is just a regular old
local variable.
And we're going to create a checking account.And let's just use the default.
So now let's test this out. We've created the c object, which is just a checking account.
And let's display that and see what we get.
Now this should still work because we overloaded those insertion operators
when we did this previously.
And as long as we're not coming at, this with a base class pointer. They should all still work.
So let's try that out. And let's see what we get.
There we go. We've got a checking account, unnamed checking account
with a 0 balance. That's exactly what we expected.
So let's create a savings account object,
and we'll call it s. And we'll initialize it to Frank
with 5000 dollar balance and
2.6% interest.
And let's display that and see what it looks like.
All right. Let's try that out.
Here we go. We've got a savings account object Frank 5000 2.6, perfect.
So this all seems to be working perfectly.
So now why don't we create a base class pointer.
So the base class is the account class, right.
So we'll create a pointer called ptr.
And let's create a new trust account.
So we'll create a new trust account on the heap, and that will be let's say Leo
with 10000 dollar balance and 2.6% interest.
So that should create that object on the heap, and the pointer will point to it.
This was perfectly valid since a trust account is an account.
Now let's display that and see what we get. Now we can't display ptr here
because that will display the address of that pointer.We need to display the object.
So we need to de-reference ptr. We need to go to where ptr is pointing and an endline here.
and an endline here.
Now this won't work as we expect.
The reason is we haven't really handled
the insertion operator to work with dynamic polymorphism.
So as far as the compiler is concerned,
that insertion operator -- compiler knows that ptr is a pointer to an account,
it really doesn't care what type of account it's pointing to. So it's going to bind this statically,
and it'll slice that object. It'll slice the trust account stuff away
and just keep the account part.
So this should still display Leo in 10000 though
because all accounts have the name of their balance. So let's give that a try
and see what our output is, and that's exactly what happens. You see right here,
account Leo 10000.
We're not getting the trust part of us, right.
It's not even displaying trust account, it's just displaying your account.
We'll fix that when we implement that i printable interface, that'll be pretty cool.
So we can see that this seems to be working. Let's test this out.
And let's just test it out with s right here. Let me comment out this Leo part here.
And let's just say that we want to deposit,
let's say,
10000 dollars into s.
And then I want to display s again.
So let's try that out. And you can see here,
I have my savings account Frank with 5000, and I just deposited 10000 into it.
It's a savings account, so it's going to use that interest rate to give me a little bonus. And that's exactly what happened.
So that looks correct. And then finally, let's withdraw.
So now let's say we want to withdraw
10000 dollars from s.
And again, we'll display s.
And we'll test out our withdrawal functionality here.
We'll give it a run. And we can see that we have
savings account 5000, then we deposited 10000,
and then we withdrew 10000. So it's all looking good.
So you see it wasn't too much work to get this to work with
dynamic polymorphism. The next step now is
let's get rid of all the overloaded insertion operators that's in this code.
And let's create that printable interface and get that working.
And then the last thing we'll do is
we'll take care of those functions in the account util file
and clean that up because there's a lot of duplicated code in there.