I don't understand why you got this weird output. When I compile, I have a correct output according to the derived structure name
As I said. It's "implementation defined" meaning it can vary between different compilers. There is no "correct output" because the standard leaves it up to the "implementation" (i.e. compiler/standard library) to decide.
I worked hard to find a good example showing how to deal with virtual function. I found something smart - a little game Paper Scissor Stone against the computer. It seems to me interesting because of some static functions/templates and a virtual function according to the player (you or the computer). I share it - maybe it could be useful for some beginners trying to understand those OOP concepts. However if you think that the code could be improved, let me know. You are skilled more than me - and your good advices help a lot ++
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <random>
enumclass choise { paper, scissor, stone };
struct utilities {
template <typename T>
static T rnd(T min, T max)
{ // avoid a crash if min is greater than max
if (min > max) std::swap(min, max);
static std::random_device seeder;
static std::mt19937 gen(seeder());
typename std::conditional<std::is_integral<T>::value,
std::uniform_int_distribution<T>, // for integral
std::uniform_real_distribution<T> // for real numbers
>::type distribution(min, max);
return distribution(gen);
}
static choise computerHand()
{ // only 3 possibilities
int h = rnd(0, 2);
if (h == 0)
return choise::paper;
elseif (h == 1)
return choise::scissor;
elsereturn
choise::stone;
}
// check if the player wins against the computer
staticbool checkParty(choise p1, choise p2)
{
if (p1 == choise::paper)
{
if (p2 == choise::scissor)
returnfalse;
if (p2 == choise::stone)
returntrue;
}
if (p1 == choise::scissor)
{
if (p2 == choise::stone)
returnfalse;
if (p2 == choise::paper)
returntrue;
}
if (p1 == choise::stone)
{
if (p2 == choise::paper)
returnfalse;
if (p2 == choise::scissor)
returntrue;
}
returnfalse;
}
// convert enum to char(s)
staticconstchar* enumToString(choise ch) throw()
{
switch (ch)
{
case choise::paper:
return"Paper";
case choise::scissor:
return"Scissor";
case choise::stone:
return"Stone";
}
return"unexpected";
}
staticvoid displayInfo()
{
std::cout << "1. Paper" << std::endl;
std::cout << "2. Scissor" << std::endl;
std::cout << "3. Stone" << std::endl;
std::cout << "Choose a hand : ";
}
};
// base class with our virtual function setChoise
class base {
private:
short score = 0;
public:
base(const std::string& name) : m_name(name) {}
~base() = default;
int getScore() { return score; }
void addScore() { ++score; }
virtualvoid setChoice() = 0;
std::string m_name;
choise m_choise {};
};
struct player1 : public base {
player1() : base("Player") {};
~player1() = default;
// rewrite the virtual function for player
virtualvoid setChoice()override
{
utilities::displayInfo();
int num;
do {
std::cin >> num;
std::cout << std::endl;
if (num == 1)
m_choise = choise::paper;
if (num == 2)
m_choise = choise::scissor;
if (num == 3)
m_choise = choise::stone;
} while (num != 1 && num != 2 && num != 3);
}
};
struct player2 : public base {
player2() : base("Computer") {};
~player2() = default;
// rewrite the virtual function for computer
virtualvoid setChoice()override {
m_choise = utilities::computerHand();
}
};
int main() {
player1 p1 = player1();
player2 p2 = player2();
do {
std::cout << p1.m_name << " " << p1.getScore() << " : " << p2.m_name << " " << p2.getScore() << std::endl;
p1.setChoice();
std::cout << p1.m_name << " plays " << utilities::enumToString(p1.m_choise) << std::endl;
p2.setChoice();
std::cout << p2.m_name << " plays " << utilities::enumToString(p2.m_choise) << std::endl;
if (p1.m_choise == p2.m_choise) {
std::cout << "Whoa! You got the same hand" << std::endl;
std::cout << std::endl;
continue;
}
if (utilities::checkParty(p1.m_choise, p2.m_choise))
{
std::cout << p1.m_name << " wins this game..." << std::endl;
p1.addScore();
}
else {
std::cout << p2.m_name << " wins this game..." << std::endl;
p2.addScore();
}
std::cout << std::endl;
} while (p1.getScore() < 3 && p2.getScore() < 3);
// only three winning games
std::cout << "Final Score :" << std::endl;
std::cout << p1.m_name << " has " << p1.getScore() << " points" << std::endl;
std::cout << p2.m_name << " has " << p2.getScore() << " points" << std::endl;
}
Player 0 : Computer 0
1. Paper
2. Scissor
3. Stone
Choose a hand : 1
Player plays Paper
Computer plays Stone
Player wins this game...
Player 1 : Computer 0
1. Paper
2. Scissor
3. Stone
Choose a hand : 2
Player plays Scissor
Computer plays Stone
Computer wins this game...
In your program human is human all the way and machine is machine; you never use "player" without knowing its type already when you compile the code.
Unrelated:
1 2 3
// OOP
player1 *p1 = new player1();
player2 *p2 = new player2();
I have to ask again: Why? Why dynamic memory allocation in this? You don't need it in this program.
Furthemore, the comment "OOP". What does it refer to?
It definitely does not refer to m_name and m_choise.
The base could offer: if ( base::sameHands(p1, p2) )
Please explain this too const std::string &winner = " wins this game...";
I wrote OOP for object. I know what it means, but I agree with you - it is a little bit unclear for other readers. Sorry.
Stack or heap - heap or stack? For this prototype, I did not think about it and I have chosen something quickly, but you are right - this memory allocation is just useless. Changed.
Good point for the sameHands function which I will put in the base class (finally I erased it and I use directly p1.m_choise == p2.m_choise).
I used a const string just to avoid the redondance in the output. Useless too. Changed.
Thanks for all these relevant points.
What did @seeplus means by "aren't assigned to a var of base type"?
Hum... I thought that using a derived class "as root" I could rewrite virtual classes in the base class. According to your explanation, now it seems to me that I had not understood the main concept. In fact, creating an object using the base class B, and associating it with a derived class D, then D can rewrite the virtual classes in B. Right? The polymorphism is done because we have two different functions in the derived classes? In my previous code, I had only a simple inheritance... I had figured out something inverted in my mind according to the relation between parent and child. Thanks for your comment @AbstractionAnon ++
I used a const string just to avoid the redondance in the output.
My bad. I did not ask about the string. I did ask about the reference in:
1 2
const std::string &winner = " wins this game...";
^
Hum... I thought that using a derived class "as root" I could rewrite virtual classes in the base class. According to your explanation, now it seems to me that I had not understood the main concept. In fact, creating an object using the base class B, and associating it with a derived class D, then D can rewrite the virtual classes in B. Right?
You refer to B * p1 = new D;?
That does not "create object using the base class B". That does "create (unnamed) class D object" (new D).
It does create a second "object" (p1), but that object is a pointer.
player1 *p1 = new player1();
player2 *p2 = new player2();
Hum... I thought that using a derived class "as root" I could rewrite virtual classes in the base class. According to your explanation, now it seems to me that I had not understood the main concept. In fact, creating an object using the base class B, and associating it with a derived class D, then D can rewrite the virtual classes in B. Right?
I thought that it could be better to use a string by its reference, but obviously I was wrong :)
Thank you @seeplus for the new code. I like it. I was not far - but yours is really well done - concise and full of clever tips which short the code, especially the checkParty() function. I used a structure for static functions because it seems to me useful, but if you think that a namespace is better, I will search some explanation about its plus on the web. In my mind, struct is like a class, but with public members as default. What about namespace? I Have to read something about it ++
I used a const because the string will not be changed.
I used a reference because I thought that it is better, but this is the same no?
They do the same work?