29

I get this error on compile -> cannot declare field M1::sc to be of abstract type I1 because the following virtual functions are pure within I1. Please help.

   class I1
    {    
    public:  
        virtual void a(int dir) = 0;
        virtual void b() = 0; 
        virtual void c() = 0; 

        void a(int dir) {  
        ....
        }

        void b() {  
        ....
        }

        void c() {  
        ....
        }
    };

    class I2 : public I1
    {    
    public:  


        void a(int dir) {  
        ....
        }

        void b() {  
        ....
        }

        void c() {  
        ....
        }
    }; 

    class M1 : public G1
    {
    protected:
    I1 sc;
    public:
       int dir = 4;
       sc.a(dir);
    };

Complete code can be found on http://pastebin.com/PFrMTJuF.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
user522767
  • 4,013
  • 6
  • 21
  • 17

3 Answers3

27

Abstract classes can't be instantiated, but you're asking the compiler to do just that by embedding an instance of I1 into every instance of M1.

You can work around that by slightly changing your design and embedding a pointer (or a smart pointer, if you can use those) to an instance of I1 instead:

class M1 : public G1
{
protected:
    I1 *sc;
public:
    M1(I1 *sc_) {
        sc = sc_;
    }
    void foo() {
        int dir = 4;
        sc->a(dir);
    }
};

EDIT: After reading your code, I think that the simplest and cleanest way to solve your problem is to pass the current room to the Execute() method of your command, e.g. something like:

class ICommand
{
public:
    virtual ~ICommand()
    {
    }

    virtual void Execute(Room *room) = 0;
};


class MoveCommand : public GameCommand
{
public:
    MoveCommand()
    {
    }

    void Execute(Room *room)
    {
        // Do something with `room`...
    }
};


void Game::HandleInput()
{
    // Read command from user and generate a command object from it.
    ICommand *pCommand = ParseCommand(Input::ReadCommand());
    if (pCommand) {
        pCommand->Execute(GetCurrentRoom());  // Pass current room to command.
        delete pCommand;
    }
}
Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • what does the sc_ represent? Can I use it just like that? – user522767 Nov 28 '10 at 10:34
  • @user, `sc_` is a constructor parameter, required to create an instance of `M1`. In my example, it should point to an instance of a concrete class derived from `I1` and providing an implementation of `a()`, `b()` and `c()`. E.g. an instance of `I2` would work. – Frédéric Hamidi Nov 28 '10 at 10:38
  • Please can I send you my code? I am new in C++ and I can't really describe everything because I have a couple of files in my project that are linked together. – user522767 Nov 28 '10 at 10:53
  • @user, you can add that code to your question (or a link to that code if it's too large), so @Konrad and the others can see it too :) – Frédéric Hamidi Nov 28 '10 at 11:02
  • I think it will be a good idea to have the abstract class because I have different derived classes for different rooms – user522767 Nov 28 '10 at 11:19
  • I just copied some of the files I think are the most important from my project. I have some left still. – user522767 Nov 28 '10 at 11:43
  • Thanks , I'll try to use the information and post my feedback here! – user522767 Nov 28 '10 at 12:01
  • I don't think that's necessary, because pointer ownership is quite explicit in your design. For instance, rooms are clearly owned by the game object and that ownership is never transfered to other objects. – Frédéric Hamidi Nov 28 '10 at 12:11
  • According to the design above, it seems I have to pass the room as a parameter to the command but according to the game, in the beginning is should be in the Jail room/cell, if i type MOVE north then i should move to the MonsterRoom and If i kill the monster and type MOVE north again , i should move to the Finish room – user522767 Nov 28 '10 at 12:15
  • I'm not allowed to pass the room – user522767 Nov 28 '10 at 12:16
  • Well, use `GetGame().GetCurrentRoom()` instead then :) – Frédéric Hamidi Nov 28 '10 at 12:17
  • In the Jail, Monster classes, there is the void method GetNextRoom(int dir) where I want to set the next room and in MoveCommand class, i can actually set the current with the GetNextRoom(int dir) , name sounds confusing though. But this should happen on typing MOVE north – user522767 Nov 28 '10 at 12:18
  • Actually I can get the current room with GetGame().GetCurrentRoom() but setting the current room with GetNextRoom(int dir) is the issue here! – user522767 Nov 28 '10 at 12:20
  • Using m_pGame->SetCurrentRoom(dir) in the MoveCommand class makes player to jump to the Finish room from the Jail Room on typing MOVE north. It skips the MonsterRoom. I need to use the same command MoveCommand in the CommandParser to move serially. first move north, to move to the monster room and after killing monster, typing MOVE north should make it move to the Finish room – user522767 Nov 28 '10 at 12:23
  • m_pGame is the pointer from the Game class when the MoveCommand class inherits from the GameCommand class – user522767 Nov 28 '10 at 12:33
  • I just need the right pointer to set the next room with the GetNextRoom(int dir) method. Apology for the confusing name. It actually sets and not getting anything as you can see from the code – – user522767 Nov 28 '10 at 12:35
  • Well, you can rename `GetNextRoom()` to `GoToNextRoom()` and use the `dir` argument to make your decision, e.g. something like `if (dir == NORTH) m_pGame->SetCurrentRoom(MONSTERROOM); else m_pGame->SetCurrentRoom(FINISH);` – Frédéric Hamidi Nov 28 '10 at 12:45
  • Like I said before, the game is serial. If in the jail cell, MOVE north is typed, player moves to the MonsterRoom because this action of moving north will do that. In the MonsterRoom, after killing mmonster and player types MOVE north again then he moves to the Finish room. Because Jail and MonsterRoom are base classes of the abstract class Room, I have to set the GetNextRoom(int dir) (well to be changed to GoToNextRoom(int dir) in both base classes and use the abstract class and movecommand to determine my new room. The setting for next room by calling GetNextRoom in MoveCommand – user522767 Nov 28 '10 at 12:53
  • dir will always be north because in the CommandParser, I have defined for east, west , south and in the movecommand, I will always use NORTH – user522767 Nov 28 '10 at 12:54
  • Another thing I thought of is, if we have the following prototype in the Room class; Room * GotoNextRoom(int dir) = 0; instead of void GotoNextRoom(int dir) and implement the new prototype as Room * GotoNextRoom(int dir) { if (dir == NORTH) {m_pGame->SetCurrentRoom(MONSTERROOM/FINISH); return m_pGame->GetCurrentRoom();}else return NULL;} in the base classes Jail and MonsterRoom. If there is a way to get the returned pointer in the MoveCommand class using *ptr->GotoNextRoom(dir) where int dir=NORTH; – user522767 Nov 28 '10 at 13:29
  • @user, you don't really need to return the current room from `GoToNextRoom()`, since it will always be accessible through `Game::GetCurrentRoom()`. Maybe you should rethink your class relationships so you can come out with a simpler design? :) – Frédéric Hamidi Nov 28 '10 at 13:33
  • I'm having difficulty using pointer which is what i'll need to set the GotoNextRoom when a player types MOVE north and invoke the MoveCommand class – user522767 Nov 28 '10 at 13:34
  • Thank you for your help. My appreciation. – user522767 Nov 28 '10 at 13:35
8

I1 is an abstract class because it has purely virtual functions (= functions without a definition).

You cannot create instances of abstract classes (because how would they work?!), therefore a declaration such as I1 a doesn’t work.

After your edit to the question it seems as though I1 shouldn’t be an abstract class, since you provided definitions for the methods. If that’s the case, just remove the = 0 after your method declarations to make the code work.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    What can i do? Please advise. – user522767 Nov 28 '10 at 10:08
  • 1
    A common workaround is to use a (smart) pointer. When you initialise that pointer, you create an instance of a non-abstract derived class. The pointer implicitly casts to the more general (but abstract) case. This is valid because the actual instance provided the method implementations - even though the pointer type doesn't know which implementations apply, that can be determined at run-time. –  Nov 28 '10 at 10:13
  • Please can I send you my code? I am new in C++ and I can't really describe everything because I have a couple of files in my project that are linked together. – user522767 Nov 28 '10 at 11:01
  • How about if a use a member initializer list? then the `I1 a` is not an instantiation, but it gets created with the derived object. – Victor Eijkhout Sep 07 '19 at 22:14
1

You can't create instance to abstract class (the class which has one or more pure virtual functions). Also there is another problem. What do you want compiler to do when you call function sc.a(dir) in class declaration? The line dir = 4 is also incorrect, only static const members of class can be initializated in class declaretion.

Mihran Hovsepyan
  • 10,810
  • 14
  • 61
  • 111
  • Actually, it would be int dir = NORTH; where NORTH is defined in one of the included files as a global variable in enum RoomId { EAST, WEST, SOUTH, NORTH} – user522767 Nov 28 '10 at 10:24