Further pure virtual calling issues

A debugbrk_ call occurs when I try to call pure virtual function Allocate_Square().
The debug call occurs on the first iteration through the inner loop.
Stepping through Allocate_Grid() with the VS debugger, all the values are correct.
When I try to step into the call to Allocate_Square(), I get the debugbrk_ call instead.
Class hierarchy:
GRID->BOARD->TBOARD
SQUARE->TSQUARE
GRID,BOARD and SQUARE classes should not be aware of TBOARD and TSQUARE.
1
2
3
4
5
6
7
8
9
using grid_t = std::array<SQUARE *, GRID_SIZE>;
class GRID
{
protected:
    grid_t      m_grid{};
    //  Overloaded by TSQUARE
    virtual SQUARE * Allocate_Square(int r, int c) = 0;
...
};


1
2
3
4
5
6
7
8
9
10
11
12
13
void GRID::Allocate_Grid()
{
    SQUARE *    sq;
    int         indx;

    for (int r=0; r<NUM_ROWS; r++)
        for (int c = 0; c < NUM_COLS; c++)
        {
            indx = OFFSET::Compute_Index(r, c);
            sq = Allocate_Square(r, c);		// <- Debugbrk_ call occurs here
            m_grid[indx] = sq;
        }
}

Allocate_Square() is not declared in BOARD, only in TBOARD.
1
2
3
4
5
6
class TBOARD : public BOARD
{
protected:
	SQUARE * Allocate_Square(int r, int c); 
...
};

What am I doing wrong?
Last edited on
Where do you call Allocate_Grid()? It would be a bad idea to do this in the constructor.

I suggest to use the keyword 'override' for virtual functions:
1
2
3
4
5
6
class TBOARD : public BOARD
{
protected:
	virtual SQUARE * Allocate_Square(int r, int c) override;
...
};
When a base function is being overridden in a derived function, then it's good practice to mark the derived function(s) as override. That way, if for any reason the function isn't overridden then the compiler will issue a compile error.

https://en.cppreference.com/w/cpp/language/override

Thanks to both of you. I made both changes and the call now works.
I'm still having issues calling pure virtual functions.
I get the following errors:

1>C:\dev\PLAYERS.cpp(142): error C2259: 'COMPUTER': cannot instantiate abstract class
1>C:\dev\COMPUTER.h(3): message : see declaration of 'COMPUTER'
1>C:\dev\PLAYERS.cpp(142): message : due to following members:
1>C:\dev\PLAYERS.cpp(142): message : 'void COMPUTER::Initialize(void)': is abstract
1>C:\dev\COMPUTER.h(14): message : see declaration of 'COMPUTER::Initialize'

In PLAYERS:
1
2
3
4
class PLAYERS 
{   
protected:
    std::array<PLAYER*, MAX_PLAYERS>    m_players{};

1
2
3
4
5
6
7
8
void PLAYERS::Initialize(int computer_players, int human_players)
{
    for (int i = 0; i < computer_players; i++)
    {
        m_players[i] = new COMPUTER(i);     // cannot instantiate abstract class occurs here
        m_players[i]->Initialize();     
    }
...

The class hierarchy is:
PLAYER->COMPUTER->TCOMPUTER
From players.h:
1
2
3
4
5
class PLAYERS 
{   
protected:
    std::array<PLAYER*, MAX_PLAYERS>    m_players{};
...

From player.h:
1
2
3
4
5
class PLAYER
{
public:
...
    virtual void Initialize() = 0;

Initialize() does not currently appear in the intermediate class (COMPUTER).
I've tried it the following ways:
- Omitted
- Simple virtual
- Pure virtual
1
2
COMPUTER::COMPUTER (int player_num) : PLAYER (player_num)
{}

From tcomputer.h:
1
2
3
4
5
class TCOMPUTER : public COMPUTER
{
public:
	void Initialize() override;
...

My objective is to call TCOMPUTER::Initialize() without PLAYER or COMPUTER classes
being aware of the TCOMPUTER class.
Last edited on
> m_players[i] = new COMPUTER(i);

Shouldn't this be: m_players[i] = new TCOMPUTER(i);
As I stated above, I don't want the PLAYER or COMPUTER classes to be aware of the TCOMPUTER class.

Edit: COMPUTER and PLAYER are in a library. TCOMPUTER is in a program. I want to be able to use the library from another PROGRAM that has a class different from TCOMPUTER.

Your post explains why when I was debugging, I could step into PLAYER::Initialize() and COMPUTER::Initialize(), but was not getting to TCOMPUTER::Initialize(). Facepalm.

Last edited on
Anybody?

Is there anyway I can instantiate TCOMPUTER without PLAYER/COMPUTER being aware of the derived class.
The line that @JLBorges mentioned is in PLAYERS, not PLAYER. So, PLAYER would not be aware of the derived class, just like you requested.

You may be attacking this problem from the wrong perspective. Maybe you can explain what you are trying to do, and we can offer suggestions on how best to do it.
doug4 wrote:
Is there anyway I can instantiate TCOMPUTER without PLAYER/COMPUTER being aware of the derived class.

Could you pass in a factory object? PLAYER can be aware of a COMPUTERFACTORY base class, that has its own virtual CreateComputer() method that returns COMPUTER*.

The code that does know about TCOMPUTER can pass to the PLAYER a TCOMPUTERFACTORY object, which inherits from COMPUTERFACTORY . TCOMPUTERFACTORY implements the CreateComputer() method to create a TCOMPUTER object and return a COMPUTER* that points to it.

That way, all the PLAYER needs to know about is COMPUTER and COMPUTERFACTORY.
Last edited on
I understand @JLBorges' fix and that does indeed fix the problem. However, PLAYERS and PLAYER are in the same library, so that does not change the problem I'm trying to solve.

I'm trying to write two versions of a program. One text oriented, the other Windows oriented.
I would like for base library to not be aware of which version is invoking the base classes.
As I stated in the first post, the class hierarchy for the text program is:
PLAYER->COMPUTER->TCOMPUTER
LIB1 LIB1 LIB2T
The class hierarchy for the Windows version is:
PLAYER->COMPUTER->WCOMPUTER
LIB1 LIB1 LIB2W
LIB2 includes LIB1. However, If I add the includes for either LIB2 to LIB1, I get a circular includes problem and I end up having to have two versions of LIB1.
Use a factory method: https://en.wikipedia.org/wiki/Factory_method_pattern

Here is an outline:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct player_collection_base // PLAYERS_BASE
{
    static constexpr std::size_t MAX_PLAYERS = 1000 ;

    protected:
        std::array<PLAYER*, MAX_PLAYERS>    m_players{};
        virtual PLAYER* create_player() const = 0 ; // factory method

    public:
        void Initialize( std::size_t num_computer_players, std::size_t num_human_players )
        {
            // validate that MAX_PLAYERS won't be exceeded

            for( std::size_t i = 0 ; i < num_computer_players ; ++i )
                m_players[i] = create_player() ; // call the factory method

            // for( std::size_t i = 0 ; i < num_human_players ; ++i ) { /* whatever */ }
            // ...
        }
};

template < typename PLAYER_IMPL >
struct player_collection /* PLAYERS */ : public player_collection_base
{
    public: virtual PLAYER* create_player() const override { return new PLAYER_IMPL(i) ; }
};

int main()
{
    using computer_player_type = TCOMPUTER ; // for the text version
    // change this to: using computer_player_type = WCOMPUTER ; // for the windows version

    player_collection<computer_player_type> players ;
    // ...
    players.Initialize( 20, 5 ) ;
    // ...
}
Topic archived. No new replies allowed.