0

first of all, I'm quite new to OOP so please bear with me...

I'm currently trying to create a Tic-Tac Toe terminal game in c++, for this, I'm trying to use a private int _size to create a 2d-array called char _board[_size][_size], but I find an error, which I don't quite understand. I did asign a value to _size on the constructor.

Invalid use of non-static data member 'Board::_size'

Board.h:

#ifndef BOARD_H
#define BOARD_H


class Board
{
    public:
        Board(int size);

        void printBoard();

    private:
        int _size;

        char _board[_size][_size];
};

#endif // BOARD_H

So, how can I solve this error, or how do you recommend I approach this problem?

2 Answers2

1

If you don't know how big the board will be at compile-time, you should use a dynamic container to contain the board data. 99% of the time, this will be std::vector.

class Board
{
    public:
        Board(size_t size); // better to use size_t than int for container sizes
        void print(); 
        size_t width() { return _board.size(); }

    private:
        std::vector<std::vector<char>> _board;
};

// This is not completely intuitive: to set up the board, we fill it with *s* rows (vectors) each made of *s* hyphens
Board::Board(size_t size) : 
                        _board(size, std::vector<char>(size, '-'))
                        {}

You can (and should!) use ranged-based for loops to show the output. This will work on vectors or built-in arrays. Defining a function of a templated class outside the class body uses this syntax:

void Board::print() { // Board::printBoard is a bit redundant, don't you think? ;)
  for (const auto &row : _board) {    // for every row on the board,
    for (const auto &square : row) {  // for every square in the row,
      std::cout << square << " ";     // print that square and a space
    }
    std::cout << std::endl;           // at the end of each row, print a newline
  }
}
Jack Deeth
  • 3,062
  • 3
  • 24
  • 39
-2

You need to allocate dynamic-size memory dynamically, but do not forget to delete it in the destructor and consider overloading your assignment operator and copy constructor (see rule of three):

#ifndef BOARD_H
#define BOARD_H


class Board
{
    public:
        Board(int size){
           _size = size;
           _board = new char*[size];
           for(int i =0; i < size; i++) 
              _board[i] = new char[size];
        }

        ~Board(){
           for(int i =0; i < _size; i++) 
              delete[] _board[i];
           delete[] _board;
        }

        Board(const Board &other){
          //deep or shallow copy?
        }

        operator=(const Board& other){
          //copy or move?
        }

        void printBoard();

    private:
        int _size;

        char **_board;
};

#endif // BOARD_H

But if you do not have to use raw memory, consider using a vector of vectors, which will take care of the memory management for you:

class Board
{
    public:
        Board(int size);

        void printBoard();

    private:
        int _size;

        std::vector<std::vector<char> > _board;
};
Jakub Zaverka
  • 8,816
  • 3
  • 32
  • 48
  • Indeed, but please manage your memory properly... – Quentin Dec 14 '16 at 16:44
  • @Quentin thanks, it was work in progress – Jakub Zaverka Dec 14 '16 at 16:48
  • Manual memory management is really nothing I would recommend, other as an exercise to learning pointers. After that it should be avoided except if needed for polymorphism. Also, your code won't compile. – Some programmer dude Dec 14 '16 at 16:50
  • 2
    `new` and `delete` were okay ten years ago. They should *not* be used at all in any code written now (except under very specific ownership semantics requirements, which a plain dynamic array does not have). – Quentin Dec 14 '16 at 16:57
  • @Someprogrammerdude In a performance-critical application, manual memory management is a requirement. Allocations are one of the most expensive operations. (Not super-germane to the question at hand, but I feel it's worth noting.) – 3Dave Dec 14 '16 at 17:00
  • Nevermind that you need `new` to construct a `shared_ptr`. (I understand what you were trying to say, but definate statements such as "NEVER USE XX" always leave out the important use cases, which still exist, and always will. Better is "Don't use X without excellent reason.") – Donnie Dec 14 '16 at 18:07
  • @Donnie (not sure if that was for me) you don't need `new` to construct a `shared_ptr`, you have `make_shared` and `make_unique`, depending on whether you want to fuse the allocation or not. – Quentin Dec 15 '16 at 08:07