-2

I have only been working with C++ for a year now and I am hoping to gain a better understanding of abstraction with my current project. I created a Black Jack card game from my book 'Beginning C++ Through Game Programming'. The game works, but I am working on a menu option that restarts the game, adds scores and allows you to view the scores via fstream.

This is what the menu section looks like inheriting from the base class Game:

class Menu
{
public:
    Menu();
    ~Menu();

private:
    Game m_game;
    //saves scores to fstream from class Player m_player;

    //menu options 
    void choice();

};

Menu::Menu() {} //no appropriate default constructor available for Game

Menu::~Menu() {}

void Menu::choice()
{
    char respond;
    bool run = true;
    cout << "Would you like to [R]estart the game, [A]dd new score, [V]iew the score sheet or [Q]uit?" << endl;
    cin >> respond;
    switch (respond)
    {
    case 'R': m_game.play(); break;
    case 'A': //add new score to fstream
    case 'V': //view score txt file
    case 'Q': run = false; break;
    }
}

I am pretty confused about how to create the default constructor for this section, even with the examples from the book.

The constructor for the Game class looks like:

class Game
{
public:
    Game(const vector<string>& names);
    ~Game();

    //plays game
    void play();

private:
    Deck m_deck;
    House m_house;
    vector<Player> m_players;

};

I'm hoping to get an explanation or example. Thank you for your help!

winterx0
  • 17
  • 7
  • 2
    You might want to change your title. Your question is not about "abstract classes" (that term has a very specific meaning in C++, and neither of the classes you have shown are abstract). Your question is actually about how to define a constructor of a class so it initialises a member of that class. In your case, the constructor of `Menu` can simply use an initialiser list to explicitly initialise the member (e.g. `Menu::Menu() : m_game(some_vector_of_strings) {}`(. (It would be advisable to initialise ALL members of `Menu` in the constructor initialiser list, but that's up to you) – Peter Mar 21 '21 at 11:23
  • 1
    Tip: Try and put your variables in the scope in which they are necessary, don't just lump them into your class for no reason. `respond` only seems to be relevant in the response function, make it a local variable. – tadman Mar 21 '21 at 11:27
  • 1
    By the way, what is `if (respond == r)` supposed to do? `r` does not appear to be initialized, and is different from `'r'` the letter. Those variables look like they correlate to the menu items, but they aren't even necessary. Tip: Use `switch` on your input. – tadman Mar 21 '21 at 11:28
  • Thank you, I will make an edit with the switch that I had originally. As for the initializer list for the constructor, I am still unsure how to properly initialize the list of members. I have tried to add `const vector& names` , `vector& name` and just `names` and it still throws out the error. I don't understand how it works in the game class when I call 'Deck' and 'House' as members without initializing them in the Game constructor, but when I try the same implementation to access 'Game', it states 'no instance of constructor matches argument list.' – winterx0 Mar 21 '21 at 11:56
  • Does this answer your question? ["No appropriate default constructor available"--Why is the default constructor even called?](https://stackoverflow.com/questions/15710125/no-appropriate-default-constructor-available-why-is-the-default-constructor-e) – Jan Wielgus Mar 21 '21 at 12:18

1 Answers1

0

Your Menu class (before calling the constructor) wants to create a Game instance using default constructor (without parameters) but Game class don't have a default constructor (because if you create any constructor, default constructor is not created automatically).

You have three options:

  1. Provide an empty constructor in your Game class next to your current constructor that gets a vector of strings.
  2. Make Game m_game; be Game m_game = Game(vector<string>());. This will be executed before the constructor and this enables you to set default values of any class member. Downside of this solution is that copy constructor will be called (first, temporary object will be made and then it will be copied to your instance, it could be optimized by the compiler but not always; if you don't know what it is, check this: https://www.geeksforgeeks.org/copy-constructor-in-cpp/)
  3. Use something that is called initializer list in c++. There are some questions in this topic on stack overflow, for example here (C++ - No appropriate default constructor available). In this solution you don't make any temporal objects (like in the first solution). Your constructor would look like this: Menu::Menu() : m_game(vector<string>()) {}.

The first and third options are the best. You have to decide which one to use.

You can replace vector<string>() with not empty vector of course.

You should also look here: https://www.learncpp.com/cpp-tutorial/constructor-member-initializer-lists/. There is explained everything about the initializer list.

The other issue here is that you shouldn't create your Game instance inside the Menu class (this is an architectural mistake). You can for example pass a pointer or reference to choice() method or Menu class constructor and store just Game instance address (go with the latter if Game instance will be used by several methods). More appropriate approach would be that game contains a Menu instance.

  • @winterx0 I make minor changes in my answer (first and third options are the best, and I added the last paragraph) – Jan Wielgus Mar 21 '21 at 12:09