c++classobject-sharing

C++ Object-sharing Errors LNK2005 and LNK 1169


I am currently trying to create a game of tic-tac-toe played alternatively by two player objects created from player class on a board object that is shared by the players. As far as I know I have completed the program, yet when I try to build I get these errors and couldnt seem to find why. I am relatively new to object-oriented programming. Here are the errors I get:

 1>Player.obj : error LNK2005: "public: __thiscall Player::Player(class Board *,char)" (??0Player@@QAE@PAVBoard@@D@Z) already defined in Connect_Four_Game.obj
 1>fatal error LNK1169: one or more multiply defined symbols found

Here are the codes:

#ifndef ___PLAYER_H___
#define ___PLAYER_H___


#include <iostream>
#include "Board.h"
using namespace std;
class Player {
private:
    Board * sharedboard;
    char player_char;
public:

    Player(Board *shared_board, const char player_char);

    bool play(int &choice);
    bool wins();
    bool columnFull(int&choice);

};
Player:: Player(Board *shared_board,const char player_char)
    : player_char(player_char), sharedboard(shared_board)
{}

#endif

Board header file:

#ifndef ___BOARD_H___
#define ___BOARD_H___


#include <iostream>
#include <vector>
using namespace std;

class Board {

public:
    vector<vector<char>> holder;
    void displayBoard();
    bool isBoardFull();
    Board()
    {
        vector<vector<char>>holder(7, vector<char>(7, 'A'));
    }
};


#endif

Player cpp file:

#include <iostream>
#include "Player.h"

using namespace std;


bool Player::columnFull(int &choice)//DONE
{
    for (int i = 6; i >= 0; i--)
    {
        if(sharedboard->holder[i][choice]=='A')//if the choice is not full
        {
            sharedboard->holder[i][choice] = player_char;//put the players character(X or O here)
            return false;
        }
        else//if the choive is full
        {

            return true;
        }
    }
}

bool Player::play(int &choice)//DONE
{ 
    if(choice < 0 )
    {
        cout << "Choice is not valid." << endl;
        return false;
    }
    else if(choice > 6)
    {
        cout << "Choice is not valid." << endl;
        return false;
    }
    else if(columnFull( choice))//if its true then its full
    {
        cout << "Choice is not valid." << endl;
        return false;
    }
    else
    {
        sharedboard->displayBoard();//if previous ones were false, then the place was empty, and filled, therefore the board will be displayed
    }
}

bool Player::wins()//if the player wins
{

        int o = 0;
        int x = 0;

        for (int i = 0; i < 7; i++)//HORIZONTAL
        {
            for (int j = 0; j < 7; j++)
            {
                if (sharedboard->holder[i][j] == 'O')
                {
                    o++;
                }
                else if (sharedboard->holder[i][j] == 'X')
                {
                    x++;
                }
                if (o == 4)//if O is four horizantily
                {
                    cout << "Player2 wins!" << endl;
                    sharedboard->displayBoard();
                    return true;
                }
                else if (x == 4)//if X is four horizantily
                {
                    cout << "Player1 wins!" << endl;
                    sharedboard->displayBoard();
                    return true;
                }
            }
        }//HORIZONTAL
        o = 0;
        x = 0;
        for (int i = 0; i < 7; i++)//VERTICALLY
        {
            for (int j = 0; j < 7; j++)
            {
                if (sharedboard->holder[j][i] == 'O')
                {
                    o++;
                }
                else if (sharedboard->holder[j][i] == 'X')
                {
                    x++;
                }
                if (o == 4)//if O is four vertically
                {
                    cout << "Player2 wins!" << endl;
                    sharedboard->displayBoard();
                    return true;
                }
                else if (x == 4)//if X is four vertically
                {
                    cout << "Player1 wins!" << endl;
                    sharedboard->displayBoard();
                    return true;
                }
            }
        }//VERTICALLY
}

Board cpp file:

#include "Board.h"
#include<iostream>

using namespace std;

void Board::displayBoard()//DONE
{
    for (int i = 0; i < 7; i++)
    {
        for (int j = 0; j < 7; j++)
        {
            cout << holder[i][j] << " ";
        }
        cout << endl;
    }
}
bool Board::isBoardFull()//DONE-CORRECTED
{
    for (int i = 0; i < 7; i++)
    {
        for (int j = 0; j < 7; j++)
        {
            if (holder[i][j] == 'A')
            {
                return false;//game continues
            }
            else if(((i==6)&&(j==6)))
            {
                return true;//ends in tie
            }
        }
        cout << endl;
    }
}

main source file:

#include<iostream>
#include<string>
#include "Board.h"
#include "Player.h"

using namespace std;

int main()
{
    Board myBoard = Board();


    /* 
       //Create your player objects based on your choice of approach to share an object.
       //The parameters "..." are left for you to set.
       Player player1 = Player(...); 
       Player player2 = Player(...);
    */
    Player player1(&myBoard, 'X');
    Player player2(&myBoard, 'O');


    int col, turn = 0;
    bool continueGame = true;
    bool validMove = false;

    while(continueGame)
    {
        myBoard.displayBoard();
        if(turn == 0)
        {
            cout << "Player 1 turn: " << endl;
            cin >> col;
            validMove = player1.play(col);

            if(player1.wins())
            {
                continueGame = false;
                cout << "Player1 won the game!" << endl;
            }
        }
        else if(turn == 1)
        {
            cout << "Player 2 turn: " << endl;
            cin >> col;
            validMove = player2.play(col);

            if(player2.wins())
            {
                continueGame = false;
                cout << "Player2 won the game!" << endl;
            }
        }

        if(continueGame) {
            //If a valid move has been done, change the turn
            if(validMove) {
                turn = (turn + 1) % 2;      
            }

            if(myBoard.isBoardFull())
            {
                continueGame = false;
                cout << "Noone won the game!" << endl;
            }
        }
    }   

    cout << "Game is over!" << endl;
    cout << "Final state of the board: " << endl;
    myBoard.displayBoard();

    return 0;
}

I did not post whole player cpp file as it is around 600 lines and has only repetition for diagonal search and nothing else. Thank you by now.


Solution

  • The error says you have multiple symbols defined. Most probably, you included the Player.cpp file (instead of Player.h) multiple times (directly or indirectly) in some other files (you did not share the Connect_Four_Game files).

    I recommend you do some research: what happens if you include .cpp files? Read about declarations, definitions, multiple inclusions, ... . This could be useful as well: Already defined in .obj - no double inclusions

    I started just like you, asking bad questions (I guess everybody did). Next time, you should try to detect where the linker error occurs, and know what a linker error is.

    I remember very well my starting days of C++. As you have obviously put effort in this code, allow me to give you some advice related to your posted code.

    Design

    Before coding, do the design properly. It's ok to have a shared board between players. You also could have a board to which the players subscribe. Either way:

    Board should know everything about itself. That is: a player should not access the attribute "holder" of the board to see if a square is being occupied or not. A player should just ask to the board: is that square free?

    In the same way, columnFull should not be a method of Player.

    Code

    You should try to use modern C++. I rewrote some of your code.

    Player constructor: use smart pointers

    Player(const std::shared_ptr<Board>& board, std::string&& name) : 
        m_name(std::move(name)), m_board(board)
    {}
    

    You main file will now look like this:

    std::shared_ptr<Board> myBoard = std::make_shared<Board>();
    Player me(myBoard,"Me");
    Player you(myBoard,"You");
    

    Reference built-in types:

    bool play(const int choice) // no reference to built-in types unless needed
    

    Use const wherever you can:

    bool wins() const // should be const, you don't want to modify "this"
    

    Use private members, string instead of char and smart pointers

    private:
        std::shared_ptr<Board> m_board; // use smart pointers
        std::string m_name; // not playerChar -> it's obvious its about the player
    

    Look how nice modern C++ is:

    void display() const // use const if you don't modify this
    {
        // you had hardcoded numbers here (7)... try to avoid that, keep the code generic
        for (const auto& v : m_holder) // you do have access to C++11 I hope?
        {
            for (const auto& h : v)
                std::cout << h << std::endl;
        }
    }
    

    Use the standard library:

    bool isFull() const // try to use the wonderful standard library, your code will be much more readable
    {
        for (const auto& v : m_holder)
        {
            auto it = std::find_if(v.begin(),v.end(),[](const std::string& h){ return h.empty(); });
            if (it==v.end())
                return false;
        }
        return true;
        // cout << endl shouldn't be in this function which checks if the board is full...
    }
    

    Maybe some typedef to keep code clean? Also, use _ or m_ for attributes. Try to keep the attributes private when possible.

    private: // try to keep members in private so you control who changes them
        // maybe use typedef? you use that type multiple times
        typedef std::vector<std::vector<std::string>> HolderType;
        HolderType m_holder; // for members, use _ or m_
    

    Overall

    1. Concentrate on the design before coding.
    2. Use modern C++, standard library, ...
    3. Use smart pointers.
    4. Learn when to use const, references, ...
    5. Keep your code clean.

    This is of course a very incomplete list of advice. But these were the main flaws of your code.

    I hope this was helpful.