0

Error

e/c++/v1/algorithm:642: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:321:9: error:

field type 'Space' is an abstract class _T2 second; ^

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:624:16: note:

Question

How can I define a std::vector of type Space which is an abstract class and then fill this vector with instances of the derived classes Empty, Snake, Ladder.

Context

I know abstract classes in C++ can not be instantiated. Instead I've read in several posts on this and other sites that you can create a collection of an abstract type if it the type is defined as a star * pointer or any of the <memory> managed pointer data types like std::unqiue_ptr<T>. I've tried to used shared_ptr<Space> in my case, but still unable to define the collection properly. I am compiled my code using g++ -std=c++17 main.cpp && ./a.out.

Code

#include <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <memory>
#include <typeinfo>
#include <queue>
#include <string>
#include <vector>

class Player
{
    private:
        int m_current_space = 1;
    public:
        Player() {}
        void role_dice() {
            m_current_space += floor( (rand()%10 + 1) / 3 );
        }
        int const get_current_space() {
            return m_current_space;
        }
        void set_current_space(int current_space) {
            m_current_space = current_space;
        }
};

class Space
{
    protected:
        int m_id;
        std::vector<Space> m_paths;
    public:
        Space() {} // requied to use [] operator in map
        Space(int id) : m_id(id) {}
        void add_path(Space& s) {
            m_paths.push_back(s);
        }
        int get_id() {
            return m_id;
        }
        virtual std::string class_type() = 0;
};
class Empty : public Space
{
    public:
        Empty(int id) : Space(id) {}
        std::string class_type() {
            return "Empty";
        }
};
class Ladder : public Space
{
    public:
        Ladder(int id) : Space(id) {}
        virtual void event(Player& p) {
            p.set_current_space(1);
        }
        std::string class_type() {
            return "Ladder";
        }
};
class Snake : public Space
{
    public:
        Snake(int id) : Space(id) {}
        virtual void event(Player& p) {
            p.set_current_space(4);
        }
        std::string class_type() {
            return "Snake";
        }
};

class Board
{
    private:
        std::map<int, Space> m_board;
    public:
        void add_space(Space& s) {
            m_board[s.get_id()] = s;
        }
        void draw_board() {
            int i = 1;
            for(auto const& [space_key, space] : m_board) {
                if(i%3 == 0) {
                    std::cout << "○\n";
                }
                else if(typeid(space) == typeid(Snake)) {
                    std::cout << "○-";
                }
                else {
                    std::cout << "○ ";
                }
                ++i;
            }
        }
        void update_player_on_board(int position) {
            int i = 1;
            for(auto const& [space_key, space] : m_board) {
                if(i%3 == 0) {
                    if (space_key == position) {
                        std::cout << "●\n";
                    }
                    else {
                        std::cout << "○\n";
                    }
                }
                else if(typeid(space) == typeid(Snake)) {
                    std::cout << "○-";
                }
                else {
                    if (space_key == position) {
                        std::cout << "● ";
                    }
                    else {
                        std::cout << "○ ";
                    }
                }
                ++i;
            }
        }
        const std::map<int, Space> get_board() {
            return m_board;
        }
        friend std::ostream &operator<<(std::ostream& os, const Board& b) {
            return os;
        }
};

class GameStateManager
{
    private:
        std::string m_state = "game over";
        bool m_playing = false;
    public:
        std::string const get_state() {
            return m_state;
        }
        void set_state(std::string state) {
            m_state = state;
        }
};

int main()
{
    std::cout << "Welcome to Bowser's 9 board game\n";
    std::cout << "Start? y(yes) n(no)\n";

        GameStateManager game_manager;
        game_manager.set_state("playing");


        auto space1 = std::make_shared<Space>(1);
        auto space2 = std::make_shared<Space>(2);
        auto space3 = std::make_shared<Space>(3);
        auto space4 = std::make_shared<Space>(4);
        auto space5 = std::make_shared<Space>(5);
        auto space6 = std::make_shared<Space>(6);
        auto space7 = std::make_shared<Space>(7);
        auto space8 = std::make_shared<Space>(8);
        auto space9 = std::make_shared<Space>(9);

        std::vector<std::shared_ptr<Space>> v {
            space1, space2, space3,
            space4, space5, space6,
            space7, space8, space9
        };

        Board bowsers_bigbad_laddersnake;
        for(int i = 0; i < 10; ++i) {
            bowsers_bigbad_laddersnake.add_space(*(v[i]));
        }
        bowsers_bigbad_laddersnake.draw_board();

        Player mario;

        int turn = 0;
        while(game_manager.get_state() == "playing") {
            std::cin.get();
            std::cout << "-- Turn " << ++turn << " --" << '\n';
            mario.role_dice();
            bowsers_bigbad_laddersnake.update_player_on_board(mario.get_current_space());
            if (mario.get_current_space() >= 9) {
                game_manager.set_state("game over");
            }
        }

        std::cout << "Thanks a so much for to playing!\nPress any key to continue . . .\n";
        std::cin.get();



    return 0;
}
greg
  • 1,118
  • 1
  • 20
  • 40
  • 3
    You cannot make (instantiate) an object of an abstract class type. (`std::make_shared`). – rranjik Apr 15 '19 at 22:37
  • How can I make a collection of an abstract class type and populate it with derived classes? – greg Apr 15 '19 at 22:38
  • 1
    You can only have a collection of pointers to abstract types (smart or otherwise). – Galik Apr 15 '19 at 22:39
  • @Galik Is this not a collection of pointers of my type Space `std::vector>` – greg Apr 15 '19 at 22:41
  • 2
    You can only attach *derived* types to those pointers (concrete derived types to be exact). – Galik Apr 15 '19 at 22:42
  • Possible duplicate of https://stackoverflow.com/questions/2160920/why-cant-we-declare-a-stdvectorabstractclass – Galik Apr 15 '19 at 22:43
  • @Greg it is. But you posted code with `std::vector` – DeviatioN Apr 15 '19 at 22:44
  • Try `std::make_shared(1)`? – Galik Apr 15 '19 at 22:45
  • @DeviatioN I cerated `std::vector> v {` in main and populated it with `shared_ptrs` of types Empty, Snake and Ladder – greg Apr 15 '19 at 22:45
  • @Galik `auto space1 = std::make_shared(1);` is this the same as your suggestion of `std::make_shared(1)`? – greg Apr 15 '19 at 22:46
  • @Greg But in `class Space` you have `std::vector m_paths` – DeviatioN Apr 15 '19 at 22:48
  • 2
    No. `std::make_shared(1)` creates a `Empty` object and `std::make_shared(1)` tries to create a `Space` object but failes because `Space` is an abstract type. – Galik Apr 15 '19 at 22:48
  • @DeviatioN the vector in main is the one I am referring too that has errors – greg Apr 15 '19 at 22:51
  • That vector should work correctly. Are you sure it is the source of the errors? – DeviatioN Apr 15 '19 at 22:54
  • 1
    @DeviatioN you were right in my `Space` and `Board` classes there where collections using the abstract `Space` class, I've changed these to smart pointers as well and the code is compiling and running now! – greg Apr 15 '19 at 23:07

1 Answers1

1

You seem to have removed a lot of code to get into details here. Have a Space pointer (smart or raw). Instantiate the specific space that you want, point to it with your pointer of type Space. Example std::shared_ptr<Space> pointerToSpace = std::make_shared<Snake> ("I'm a snake"); Now, without loss of generality, you can print the contents (of concrete type) with just the pointer to the space pointerToSpace->class_type(). Yes, you can have a collection of shared_ptrs in a container.

rranjik
  • 690
  • 9
  • 21