C++/RE: Basketball Bet Program
Expert: Zlatko - 12/3/2010
QuestionQUESTION: Hi Zlatko once again,
This program involves calculating winnings/losses for
basketball bets where the betting company adds the following
bonus percentages to the winnings.
0.5% bonus for winnings of $200 or less, 1% to winnings of
200.01 to $1500, and 1.5% for 1500.01 or more.
Win 90% of bet when picking the correct winner, else lose
the bet. For example, if the bet is $100, the bettor wins
$90 if correct or loses $100 if wrong. The two teams wer'e
looking at are named Shamrocks and Shakers.
There are also other bet types I need to include in this
program, but I'm only doing this first one (as the rest
should be similar after I finally get this one working)
Also, program should be using virtual functions including
at least one pure virtual function with polymorphism. And
also one class for each bet type with a base class
containing appropriate data members and methods should also
be included.
Here's where my program is at the moment, don't want to get
too far ahead of myself.
Is this right --> virtual functions allow you to reuse code
but change it as necessary. For example, a building class
may have a function for building a gray house. Now a
apartment class (derived) from building class the apartment
may need three windows and a door. This is then where we can
use virtual functions? And pure virtual function, they do
the same except it's an empty function with nothing in the
body? But why you want to do that? That I'm a little
confused.
Finally, I wanted to write the calcBonus so that it appears
only once, meaning, I have the function already used once,
and for the rest of the bet types, I would simply recall
this function as the you always calculate the Bonus with the
same formula set.
Thank you very much!
#include<iostream>
using namespace std;
class Bet
{
public:
Bet(const char*, const char*);
~Bet();
virtual float calcBonus() const = 0;
virtual void print() const;
private:
char *firstTeam;
char *secondTeam;
float betAmt;
};
Bet::Bet(const char *firstTeam_name, const char
*secondTeam_name)
{
firstTeam = new char[strlen(firstTeam_name) + 1];
strcpy(firstTeam, firstTeam_name);
secondTeam = new char[strlen(secondTeam_name) + 1];
strcpy(secondTeam, secondTeam_name);
}
void Bet::print() const
{
cout << firstTeam << ' ' << secondTeam;
}
Bet::~Bet()
{
delete[] firstTeam;
delete[] secondTeam;
}
class Winner: public Bet //Winner class derived from
Bet class
{
public:
Winner(const char*, const char*, int = 0, int = 0);
virtual float calcBonus() const;
virtual void print() const;
private:
int firstTeamScore;
int secondTeamScore;
};
Winner::Winner(const char *firstTeam_name, int team1Score,
int team2Score, const char *secondTeam_name, int team1Score,
int team2Score)
:Bet(firstTeam_name, secondTeam_name) // call base-class
constructor
{
firstTeamScore = team1Score;
secondTeamScore = team2Score;
}
float Winner::calcBonus() const
{
if(betResult <= 200)
return 0.5/100 * betResult;
if(betResult >= 200.01 && betResult <= 1500)
return 1/100 * betResult;
if(betResult >= 1500.01)
return 1.5/100 * betResult;
}
int main()
{
Winner w("Shakers", 90, "Shamrocks", 88, 100);
return 0;
}
ANSWER: Hello Mike.
Virtual functions allow you to write code that uses some base class. At run time, you can pass a derived class to that code, and when that code calls virtual methods, it is code in the derived classes that gets executed. I explain this in more detail, and with code samples at this page:
https://sites.google.com/site/zlatkoscodingtopics/virtual-function-basics
When you have inheritance, the behavior of method calls changes depending on if you are calling the base version of a class, the derived version of a class, or if you are calling through a base reference or a derived reference. Adding virtual functions complicates it even more. The best way to learn is to create a base and derived class with some methods and access them in different ways. Below is such a program. I've included comments about which method is called, and why. It is important that you understand each case.
#include <iostream>
using namespace std;
class Base
{
public:
void a() { cout << "Base::a\n";}
void b() { cout << "Base::b\n";}
virtual void va() { cout << "Base::va\n";}
virtual void vb() { cout << "Base::vb\n";}
};
class Derived : public Base
{
public:
void b() { cout << "Derived::b\n";}
virtual void vb() { cout << "Derived::vb\n";}
};
int main(void)
{
Base b;
Derived d;
// The comments show the output
b.a(); // Base::a because b is a Base instance
b.b(); // Base::b because b is a Base instance
b.va(); // Base::va because b is a Base instance
b.vb(); // Base::vb because b is a Base instance
cout << "---\n";
d.a(); // Base::a because Derived does not have an a
d.b(); // Derived::b because d is an instance of Derived
d.va(); // Base::va because Derived does not have a va
d.vb(); // Derived::vb because d is an instance of Derived
cout << "---\n";
Base& br = d; // assign a Derived instance to a Base reference
br.a(); // Base::a for the same reason as the br.b() example below. Also
// Derived does not have an a method.
br.b(); // Base::b Although the actual type is derived,
// the b method is non virtual, and is being
// called through a base reference, so the Base version of b is called.
br.va(); // Base::va, because Derived does not have a va.
br.vb(); // Derived::vb. Because vb is virtual, and the actual type is Derived,
// it is the Derived vb that is called
// even though we are calling it through a Base reference.
// Compare this behaviour with the call of br.b().
// The above example is the purpose of virtual functions
cout << "---\n";
}
The way you describe virtual functions when you say:
"Is this right --> virtual functions allow you to reuse code
but change it as necessary."
is true, but not much different from non virtual functions. For example, in the above code, I have non-virtual methods "a" and "b" in Base, which can be reused in Derived, but Derived changes method "b" by supplying its own. The real power of virtual functions is in accessing a derived class' method through a base class reference (or a base class pointer).
Pure virtual functions are like virtual functions but they require a derived class to provide an implementation. Often, the base class is used like an interface to all the derived objects. In such cases, the Base class will not have any data, and so it would not be logical for the Base class to provide an implementation of the virtual functions.
If your calcBonus function is really the same across all bet types, then you can put it into your Bet base class as a virtual method and all others will inherit it. If there is a Loser bet type, then you would probably want to override the calcBonus implementation in the Loser bet.
It might make sense to make the print method pure virtual to require each Bet type to provide an implementation.
I hope that helps you understand the purpose of virtual functions. This has been a long answer for me, and actually quite a challenging one so I hope it is clear. Please do ask again about anything that is unclear.
Best regards
Zlatko
---------- FOLLOW-UP ----------
QUESTION: Hi Zlatko,
Thanks for the explanation, I guess though, it would take
some more practice to get everything straight. Sorry if I
sound a little confused at times, because this stuff is not
exactly a walk in the park for me so to say.
Anyway, as per my assignment here (See code below) I sort of
rewrote it a bit renamed some of the variables and now, I do
have a virtual function calcBonus defined in the base class
and I have a the calcBonus function there too. Assuming I am
on the right track here, as far as I see, the problem is
where I may get the calcBetResult information from because I
understand the calcBonus if statements can't do the proper
calculations.
This calcBetResult, would be the Bet calculation (for this
one bet type I described above (which I will call the Winner
bet type), the winning 90% of bet if the team you bet on
wins else you lose all your money). I see that getting this
fixed would then allow me to just say Bet::calcBonus in a
derived class, and it would just go to the base class and
use the calcBonus function that is already defined there (As
you use the same formula every time you need to Calculate
the Bonus.)
And also, I'm not getting something --> how would you
actually send the parameters properly out to the part of my
code where I want to compare the team names so if the
winning team's name is the same as the name of the team I
betted on (in other words, I have picked the winner), the
calculation for my winnings would take place, else I'd do
the other calculation (the losing side). Here, I wanted to
send in the team names, but I have a feeling we need to get
it from above, or I'm doing something wrong or is there a
better way of it that's not as confusing?
Thanks once again!
Mike
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
class Bet
{
public:
Bet(const char *, const char *);
~Bet();
virtual float calcBetResult() const = 0;
virtual float calcBonus() const;
virtual void print() const;
private:
char *team1;
char *team2;
};
Bet::Bet(const char *team1_name, const char *team2_name)
{
team1 = new char[strlen(team1_name) + 1];
strcpy(team1, team1_name);
team2 = new char[strlen(team2_name) + 1];
strcpy(team2, team2_name);
}
float Bet::calcBonus() const
{
if(calcBetResult <= 0)
return 0;
if(calcBetResult <= 200.0)
return 0.05 / 100 * calcBetResult;
if(calcBetResult >= 200.01 && calcBetResult <= 1500)
return 1 / 100 * calcBetResult;
if(calcBetResult >= 1500.01 && calcBetResult <=
7500)
return 1.5 / 100 * calcBetResult;
if(calcBetResult >= 7500.01)
return 2 / 100 * calcBetResult;
}
void Bet::print() const
{
cout << team1 << ' ' << team2; //need to add more
stuff to here, we're not only printing team names!
}
Bet::~Bet()
{
delete[] team1;
delete[] team2;
}
class Winner: public Bet
{
public:
Winner(const char*, int, const char*, int, const
char*, float = 0.0);
virtual float calcBonus() const;
virtual float calcBetResult() const;
virtual void print() const;
private:
float WinnerBetAmt;
};
Winner::Winner(const char *team1_name, int team1_score,
const char *team2_name, int team2_score, const char
*team_picked, float betAmt)
:Bet(team1_name, team2_name)
{
WinnerBetAmt = betAmt;
}
float Winner::calcBetResult(/*const char *team1_name, int
team1_score, const char *team2_name, int team2_score, const
char *team_picked, float betAmt*/) const
{
if((strcmp(team1_name, team_picked) == 0) ||
(strcmp(team2_name, team_picked) == 0))
return 90 / 100 * WinnerBetAmt;
else if((strcmp(team1_name, team_picked) != 0) ||
(strcmp(team2_name, team_picked) != 0))
return -WinnerBetAmt;
}
int main()
{
cout << fixed << showpoint << setprecision(2);
Winner w("Shakers", 90, "Shamrocks", 88, "Shakers",
100.00);
return 0;
}
AnswerHello Mike.
Thank you for sending me the complete assignment text. It makes things much clearer. I now understand that the Winner bet is a type of betting scheme, and not a win vs. loss, so things are much clearer.
You are on the right path.
I would make these suggestions.
1) Put all the data into the base Bet class as protected, so that the derived classes can access it. This means teams, scores, and winning team. I suggest you use the std::string datatype instead of character arrays. It is safer, simpler (less code = fewer chances for bugs), and more professional.
2) Each bet type has a different payoff calculation algorithm, so that must be a pure virtual method in the Bet class. You were on the right path. The base Bet class does not know about how to calculate payoff. Each derived class will access the data in the Bet class to determine payoff. You can actually put the payoff as a data member in the base Bet class since every bet has a payoff of some sort.
3) You can put the print method into the base class, because all the data that will be printed is in the base class, and also the printout is the same for all bet types with the exception that the text for the bet type is not known by the base class. You need a virtual function which will return the text of the bet type to the print method. You can make it a pure virtual as well.
4) The calcBonus function is not virtual and is common to all bets. You were on the right to make it common in the base class. It is called upon by the derived classes when the bet payoff is positive. It only depends on the winnings. I would pass the winnings to it as a parameter. If you do that, you will see that calcBonus does not need any other data, and so can be made static. If you are not familiar with static methods, don't worry about it.
The assignment does not say that you need virtual functions and at least 1 pure virtual. In my opinion, that means that all virtual function can be pure virtual, but if you want at least one that is not pure virtual, you can make the betName method virtual, but not pure.
With these suggestions, your declarations look like this:
class Bet
{
public:
Bet(const std::string& team1, int score1,
const std::string& team2, int score2,
float betAmount,
const std::string& winningTeam);
// dont need this anymore ~Bet();
// this will calculate and set mWinnings, no return type is needed
virtual void calcBetResult() = 0; // derived class to supply algorithm
virtual const char* betName() const = 0;
void print() const;
static float calcBonus(float winning);
protected: // These data members will be visible to derived classes
std::string mTeam1;
int mScore1;
std::string mTeam2;
int mScore2;
std::string mWinningTeam;
float mBetAmount;
float mWinnings;
};
class Winner: public Bet
{
public:
Winner(const std::string& team1, int score1,
const std::string& team2, int score2,
float betAmount,
const std::string& winningTeam);
virtual void calcBetResult();
virtual const char* betName() const { return "Winner";}
};