6

flowchart

I'm having trouble figuring out how to properly create an object depending on the user's choice.

In the program, I ask the user which they class they want to be--Knight or Wizard. I take input '1' or '2' to represent Knight and Wizard.

I made a switch statement, and within case 1, I declared an object Knight, and the same for Wizard.

I need to use these objects outside of the switch statement, but I can't. I tried to make a 'default' object by making 'Player player;' but because the Player class has a pure virtual function, I can't do that either.

How do I do this effectively?

This is what I have so far:

int main()
{
std::string plyrName;
int input;
bool foo = false;

std::cout << "What is your name?\n";
std::cin >> plyrName;
std::cin.ignore(1000, '\n');

std::cout << "\nWelcome, " << plyrName << ". What class would you like to be?\n";
std::cout << "1. Knight.\n2. Wizard.\n";
std::cin >> input;

while (input != 1 && input != 2)
{
    if (foo == true)
        std::cout << "Please enter 1 for Knight and 2 for Wizard.\n";
    if (!(std::cin >> input))
    {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "\n";
        std::cout << "Only integers are allowed.\n";
    }
    else
        std::cout << "\n";
    foo = true;
}

switch (input)
{
case 1:
{
    Wizard player;
    break;
}
case 2:
{
    Knight player;
    break;
}
}


std::cout << "\nHere is your player information summary.\n";
std::cout << player.classType();

system("pause");
return 0;
}

I need to access the player object after is has been created, because I want to output to the user which class they selected. Both Knight and Wizard classes have a function to output this.

EDIT: I have a follow up question. In the diagram, Knight & Wizard have a static variable 'special attack name'. How can I access this variable in the main function? The solution of using unique_ptr means that the pointer will point to the base class Player, thus not allowing access to the derived class members such as the static variable 'special attack name'. Do I have a flaw in my design?

Ramin Amiri
  • 151
  • 1
  • 3
  • 12
  • 5
    Perhaps using pointers (preferably [smart pointers](https://en.cppreference.com/w/cpp/memory#Smart_pointers))? That's how you usually handle polymorphism in C++. – Some programmer dude Jan 28 '19 at 14:50
  • 1
    you need pointers or references anyhow if you want to use polymorphism, maybe fix that first, because answering your question literally will just lead you to the next problem – 463035818_is_not_an_ai Jan 28 '19 at 14:51
  • @user463035818 What do you mean by "fix that first" and answering this question will lead to the next problem? How would I go about fixing this if I want to use polymorphism and prevent leading to another problem? – Ramin Amiri Jan 28 '19 at 15:08
  • I meant that, one could take your question literally and write a "solution" that creates either a `Knight` or a `Wizard` object, but that wont help you to treat them both as a `Player` unless you use pointers or references. Note that this is already taken into account by the answers (well 2 out of 3 to be precise ;) – 463035818_is_not_an_ai Jan 28 '19 at 15:26

3 Answers3

6

In your case, because you want to accomplish polymorphism, you should go for pointers and references. Why? I would highly recommend this beautiful answer. Why doesn't polymorphism work without pointers/references?

So, should you go for a raw pointer, something like Player *?

In almost all scenarios, you should never ever go for raw pointers and especially, when it points to dynamic memory. Simply because any programming error or an exception might lead to delete getting skipped.

Therefore, I would highly recommend you to go for smart pointers introduced in C++11 like unique_ptr and shared_ptr which follow RAII pattern and guarantee deinitialization.

Here is an example of usage of unique_ptr in your case.

#include <memory>

using PlayerPtr = std::unique_ptr<Player>;
using KnightPtr = std::unique_ptr<Knight>;
using WizardPtr = std::unique_ptr<Wizard>;

int main()
{
    ...
    PlayerPtr playerPtr = nullptr;

    switch (input) {
        case 1: {
             playerPtr = KnightPtr(new Knight);
        }
        break;

        case 2: {
             playerPtr = WizardPtr(new Wizard);
        }
        break;
    }

    // use playerPtr outside.
}

Edit:

As rightly pointed out by HTNW, you must go for std::make_unique instead of using new. But remember, that is a C++14 concept. You must have compiler support for it.

Kunal Puri
  • 3,419
  • 1
  • 10
  • 22
  • Thank you for this! Is this an effective way of doing this in general? Or should I have done this in a completely different structure if I had just started making these program? – Ramin Amiri Jan 28 '19 at 15:05
  • 3
    This answer would be better if it [recommended using std::make_unique over new](https://stackoverflow.com/q/22571202/5684257) and explained why we need to use a pointer at all. – HTNW Jan 28 '19 at 15:06
  • 1
    I think you should go for a factory pattern instead of having a switch case. It will help you in the long run. – Kunal Puri Jan 28 '19 at 15:07
  • @HTNW I have tried to improve my answer. Hope it looks much better than the one previously. – Kunal Puri Jan 28 '19 at 15:10
  • @KunalPuri Because the switch statement isn't an actual implementation of a factory? – Jean-Baptiste Yunès Jan 28 '19 at 15:18
  • @Jean-BaptisteYunès It depends how you implement it. I would have implemented it using `map` instead of a switch case. – Kunal Puri Jan 28 '19 at 15:18
  • @KunalPuri What is a factory (basic says)? A piece of code that constructs a polymorphic object. Then that switch does exactly the job, no need to make it more complex for OP that is currently learning... – Jean-Baptiste Yunès Jan 28 '19 at 15:23
  • @Jean-BaptisteYunès I agree. That's why I said in the long run. Applications become bulky almost instantly and then, maintainence becomes a worry. If design patterns are incorporated correctly in the initial phase, it saves a lot of time and effort. – Kunal Puri Jan 28 '19 at 15:27
  • I have a follow up question if possible. In the diagram, you can see Knight & Wizard have a static variable 'special attack name'. How can I access this variable in the main function? The playerPtr pointer can only point to members of the base class. Do I have a flaw in my design? – Ramin Amiri Jan 28 '19 at 18:10
  • @RaminAmiri It may be. I would really like to know what are you trying to do. – Kunal Puri Jan 28 '19 at 23:54
  • I uploaded my zipped project to dropbox if you're interested @KunalPuri https://www.dropbox.com/s/zgpwxt5v3bac30y/GAME1011_Lab1_AmiriRamin.zip?dl=0 I still have the issue. – Ramin Amiri Jan 29 '19 at 05:07
  • @KunalPuri Sorry, I got the link to the wrong project. Here is the correct project. https://www.dropbox.com/s/bsgbfmttlape6ve/GAME1011_Assignment1_AmiriRamin.rar?dl=0 – Ramin Amiri Jan 30 '19 at 19:49
1

if you create your variable inside the switch case scope it will get deleted as soon as you leave that scope leading you to UB so you declare it as a pointer so it can outlive the conditional statement ie : you declare it as the base class pointer before & you give it to where it points inside conditional statement

#include<memory>
int main()
{
std::string plyrName;
int input;
bool foo = false;
//create your player ptr
std::unique_ptr<Game_Object> player;
std::cout << "What is your name?\n";
std::cin >> plyrName;
std::cin.ignore(1000, '\n');

std::cout << "\nWelcome, " << plyrName << ". What class would you like to be?\n";
std::cout << "1. Knight.\n2. Wizard.\n";
std::cin >> input;

while (input != 1 && input != 2)
{
    if (foo == true)
        std::cout << "Please enter 1 for Knight and 2 for Wizard.\n";
    if (!(std::cin >> input))
    {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "\n";
        std::cout << "Only integers are allowed.\n";
    }
    else
        std::cout << "\n";
    foo = true;
}

switch (input)
{
case 1:
{   // initialize it  and it would work perfectly as you intend
    player = std::make_unique<WIZARD>();
    break;
}
case 2:
{   //****
    player = std::make_unique<KNIGHT>();
    break;
}
}


std::cout << "\nHere is your player information summary.\n";
std::cout << player->classType();

system("pause");
return 0;
}
Spinkoo
  • 2,080
  • 1
  • 7
  • 23
0

Someone correct me if I'm wrong, but objects are only valid within the scope they are created so once you are out of that switch statement those objects are not accessible anymore.

You should declare a GAME OBJECT class object outside the switch statement and then create either the wizard or the knight (as both classes inherit GAME OBJECT).

gsamaras
  • 71,951
  • 46
  • 188
  • 305
KostasKoufos
  • 41
  • 1
  • 5
  • 2
    That's true for local variables, but memory (variables) obtained with an allocator (using the keyword 'new') will last until they're freed explicitly. To ease the use of those allocated variables, the C++11 standard introduced smart pointers. – AlexG Jan 28 '19 at 14:57
  • 2
    Re: "correct me if I'm wrong" -- you're half right. **Named** objects have block scope; they go away at the end of the block in which they are created. Your solution isn't quite right. You need to create a **pointer** to a `GAME_OBJECT`, and create either a `WIZARD` or `KNIGHT` and store its address in that pointer. The object can't be a named object, because then it would go away. That's why we have `new`. – Pete Becker Jan 28 '19 at 14:57
  • 3
    read about "object slicing", what you wrote is almost correct, it just wont work with objects – 463035818_is_not_an_ai Jan 28 '19 at 15:05