How to use variables in other class members?

Pages: 12
hmm, ok. Could you show me a working example of this:

1
2
3
4
void Game::draw_player(){
    auto pos = this->player.get_position();
    this->gme_mapArray[pos.x][pos.y] = PlayerRepresentation;
}


with using the code i have above? I'll understand it far better if i can see a working example. I'ts just extremely hard for me to get something through explanation, I need to see it in action to be able to understand.

My new code. I've used pair a few times before with maps but I dont think this is how you would use it with your example:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>

using namespace std;


class Player;

class Game
{
    public:
        Game(Player player);
        ~Game();

        void DrawPlayer();


    private:
        char mapArray[0][0];
        char mapTile = '-';
};

void Game::DrawPlayer()
{

}

class Player
{
    public:
        Player(Game* game);
        ~Player();

        void PlayerPosition(pair<int, int>);


    private:
        int posX = 0;
        int posY = 0;
        char playerChar = 'o';
};

void Player::PlayerPosition(pair<int, int> pos)
{

}

int main()
{
    cout << "Hello world!" << endl;
    return 0;
}
Why does the Player constructor accept a game pointer?

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
class Player
{
    public:
        Player();
        

        void setPlayerPosition(const int x, const int y);
        std::pair<int,int> getPlayerPosition();


    private:
        std::pair<int,int>coords;
        char playerChar = 'o';
};
Player::Player()
{
coords.first=0;
coords.second=0;//set coordinates to whatever starting point in the grid that you want
}
void Player::setPlayerPosition(const int x,const int y)
{
    coords.first=x;
    coords.second=y;
}

std::pair<int,int> Player::getPlayerPosition() 
{
return coords;
} 


I don't know why Game accepts a player in the constructor either, theres nothing in the Player object that can vary before the start of the game.

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Game
{
    public:
        Game();
        ~Game();

        void DrawPlayer();
        void movePlayerUp();
        void movePlayerDown();
        void movePlayerLeft();
        void movePlayerRight();
       


    private:
        const int COL=10;
        const int ROW=10;
        bool boundCheck(const int, const int)
        void updateGame(const int,const int);
        char mapArray[ROW][COL];
        char mapTile = '-';
        Player player;

};

void Game::movePlayerUp() 
{
    std::pair<int,int>coords=player.getPlayerPosition();

   if(boundCheck(coords.first,coords.y+1)) 
   {
     updateGame(coords.second,coords.y+1);
   }  

}

bool Game::boundCheck(const int x, const int y) //check grid boundary or other barriers on grid.
{
  if(y >= 0 || y < ROW && x >= 0 || x < COL)
{
  return true;
}

return false;
}

void Game::updateGame(const int x,const int y)
{
   std::pair<int,int>oldCoords=getPlayerPosition();
   mapArray[oldCoords.second][oldCoords.first]='-';
   mapArray[y][x]='o';
   player.setPlayerPosition(x,y);
}
Last edited on
that code wont compile. theres a ton of errors. I can only seem to fix a few. as for why the class objects were the way they were, its just advice i got on another forum.
Last edited on
Would something like this be ok as well?

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>

using namespace std;

class B;

class A
{
    public:
        A() = default;
        ~A() = default;

        void AMethod(B&);
        void AMethodText(){cout << "This text is in class A" << endl;}

        private:
};

class B
{
    public:
        B() = default;
        ~B() = default;

        void BMethod(A&);
        void BMethodText(){cout << "This text is in class B" << endl;}

    private:

};

void B::BMethod(A& a)
{
    a.AMethodText();
}

void A::AMethod(B& b)
{
    b.BMethodText();
}

int main()
{
    A a;
    B b;

    a.AMethod(b);
    b.BMethod(a);

    return 0;
}
Last edited on
bump, still need help with this.
I think you need to step back, learn some more and revisit this project in the future. Having 2 objects that accept each other in their respective constructors just makes no sense, and you need to realize that.
Last edited on
If the class cannot accept a member of another class thats fine. I'm just trying to do this the right way. So basically the only way to do what i want to do is either use inheritance, friend, or make the variables part of the other class correct?
No, just use getters (in this case).
Ok perfect, I used getters and setters to access the data in the other classes and that worked beautifully. I avoided them because of the stigma around getters and setters. People say their evil, but I think that they're just over used. I think that as long as your not using them for every single variable in your classes your fine as long as you use them sparingly and when you really need them. I think i'll just try making small programs with classes before i try to do what I want to do.
Last edited on
Like most other things in programming, getters and setters are just tools. They can be used right and wrong. The problem with them is that if you use them improperly you can weaken encapsulation, increase coupling, and make the program more difficult to modify. But sometimes you really do need to give other classes read access to a member. In those cases getters and setters are often preferable to other choices, such as friend classes, because they give you more control over how other classes may access the data.
Just a short example based on your original post:
Point.h:
1
2
3
4
5
6
7
8
9
10
#ifndef POINT_H
#define POINT_H

class Point {
public:
    int row {},
        col {};
};

#endif // POINT_H 


ObjOnGround.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef OBJONGROUND_H
#define OBJONGROUND_H

#include <memory>
#include "Point.h"
#include "GameGround.h"

class ObjOnGround {
public:
    Point pos;
    int appearance {' '};
    int movement;
    GameGround* ground { nullptr };
    
    ObjOnGround(int movement_arg = 1);
    void bindToGround(GameGround* ground_arg);
    bool placeOnMap(const Point& pos);
    bool moveOnMap(int direction);
};

#endif // OBJONGROUND_H 


ObjOnGround.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "ObjOnGround.h"

ObjOnGround::ObjOnGround(int movement_arg) : movement { movement_arg } {}

void ObjOnGround::bindToGround(GameGround* ground_arg)
{
    ground = ground_arg;
}

bool ObjOnGround::placeOnMap(const Point& newpos)
{
    if(ground->placeIfFree(newpos.row, newpos.col, appearance)) {
        pos = newpos;
        return true;
    }
    return false;
}

bool ObjOnGround::moveOnMap(int direction)
{
    return ground->moveIfPossible(pos.row, pos.col, direction, 
                                    movement, appearance);
}


Rock.h:
1
2
3
4
5
6
7
8
9
10
11
#ifndef ROCK_H
#define ROCK_H

#include "ObjOnGround.h"

class Rock : public ObjOnGround {
public:
    Rock();
};

#endif // ROCK_H 


Rock.cpp:
1
2
3
4
5
6
#include "Rock.h"

Rock::Rock() : ObjOnGround(0)
{
    appearance = '#';
}


Player.h:
1
2
3
4
5
6
7
8
9
10
11
#ifndef PLAYER_H
#define PLAYER_H

#include "ObjOnGround.h"

class Player : public ObjOnGround {
public:
    Player();
};

#endif // PLAYER_H 


Player.cpp:
1
2
3
4
5
6
7
#include "Player.h"
#include <iostream>

Player::Player() : ObjOnGround(1)
{
    appearance = '*';
}


GameGround.h:
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
#ifndef GAMEGROUND_H
#define GAMEGROUND_H

#include <iostream>
#include <vector>

class GameGround {
public:
    int tile {};
    std::vector<std::vector<int>> gmap;

    GameGround();
    void drawPlayGround(int rows, int cols);
    bool placeIfFree(int row, int col, int appearance);
    bool moveIfPossible(int& row, int& col, int direction,
                            int movement, int appearance);
    
    friend std::ostream& operator<<(std::ostream& os, const GameGround& rhs)
    {
        for(const auto& v : rhs.gmap) {
            for(auto i : v) { os << static_cast<char>(i); }
            os << '\n';
        }
        return os;
    }
};

#endif // GAMEGROUND_H 


GameGround.cpp:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "GameGround.h"

GameGround::GameGround() : tile { '.' } {}

void GameGround::drawPlayGround(int rows, int cols)
{
    gmap.resize(rows, std::vector<int>(cols, tile));
}

bool GameGround::placeIfFree(int row, int col, int appearance)
{
    if(gmap.at(row).at(col) == tile) {
        gmap.at(row).at(col) = appearance;
        return true;
    }
    return false;
}

bool GameGround::moveIfPossible(int& row, int& col, int direction,
                                    int movement, int appearance)
{
    // Protect the references until we are sure:
    int newrow { row }, newcol { col };

    // Check for boundaries:
    switch(direction) {
    case 1: // North
        newrow -= movement;
        if(newrow < 0) { return false; }
        break;
    case 2: // South
        newrow += movement;
        if(gmap.size() - 1 < static_cast<size_t>(newrow)) { return false; }
        break;
    case 3: // West
        newcol -= movement;
        if(newcol < 0) { return false; }
        break;
    case 4: // East
        newcol += movement;
        if(gmap.at(0).size() - 1 < static_cast<size_t>(newcol)) { return false;}
        break;
    default:
        return false;
        break;
    }

    // Check if cell is free:
    if(gmap.at(newrow).at(newcol) != tile) { return false; }

    // Ok, we can move the object on map.
    // Delete object from previous position::
    gmap.at(row).at(col) = tile;
    // Put it in the new position:
    gmap.at(newrow).at(newcol) = appearance;

    // Inform the object about its new position:
    row = newrow;
    col = newcol;
    return true;
}


Game.h:
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
#ifndef GAME_H
#define GAME_H

#include <random>
#include <vector>
#include "GameGround.h"
#include "Rock.h"
#include "Player.h"

class Game {
public:
    GameGround ground;
    std::mt19937 rng;
    std::vector<Rock> rocks;
    std::vector<Player> players;

    Game();
    void setBasicPlayGround(int rows, int cols);
    void addRocksToGround(int howmany = 0);
    void addPlayersToGround(int howmany = 1, int appearance = 0);
    void printGround();
    void play();
};

#endif // GAME_H 


Game.cpp:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "Game.h"
#include <chrono>
#include <iostream>
#include <string>

Game::Game() {}

void Game::setBasicPlayGround(int rows, int cols)
{
    rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    ground.drawPlayGround(rows, cols);
}

void Game::addRocksToGround(int howmany)
{
    // Keep the ground fairly empty.
    // Since this is a simplified example, in case the request were to high
    // (let's say 99 rocks on a 10 x 10 grid), it could take a very long time
    // to find randomly the last 2 or 3 free cells.
    int cells = ground.gmap.size() * ground.gmap.at(0).size();
    if(!howmany) { // no choice means 10% of rocks
        howmany = cells / 10;
    } else if(cells / 2 < howmany) { // anyway, no more than 50%
        howmany = cells / 2;
    }

    std::uniform_int_distribution<> rrow(1, ground.gmap.size() - 1);
    std::uniform_int_distribution<> rcol(1, ground.gmap.at(0).size() - 1);
    for(int i{}; i<howmany; ++i) {
        Rock r;
        r.bindToGround(&ground);
        bool ok = r.placeOnMap( {rrow(rng), rcol(rng)} );
        if(ok) { rocks.push_back(r); }
        else   { --i; } // try again
    }
}

void Game::addPlayersToGround(int howmany, int appearance)
{
    std::uniform_int_distribution<> rrow(1, ground.gmap.size() - 1);
    std::uniform_int_distribution<> rcol(1, ground.gmap.at(0).size() - 1);
    for(int i{}; i<howmany; ++i) {
        Player p;
        p.bindToGround(&ground);
        if(appearance) { p.appearance = appearance; }
        bool ok = p.placeOnMap( {rrow(rng), rcol(rng)} );
        if(ok) { players.push_back(p); }
        else   { --i; } // try again
    }
}

void Game::printGround() { std::cout << ground << '\n'; }

void Game::play()
{
    int choice {};
    do {
        for(size_t i{}; i<players.size() && choice!=5; ++i) {
            printGround();
            do {
                std::cout << "Player " << i+1 
                          << " (x: " << players.at(i).pos.col
                          << "; y: " << players.at(i).pos.row
                          << "), please make your move:\n"
                             "1) go North (up)\n2) go South (down)\n"
                             "3) go West (left)\n4) go East (right)\n"
                             "5) Exit\n> ";
                std::string line;
                std::getline(std::cin, line);
                try {
                    choice = std::stoi(line);
                } catch(std::logic_error& e) {
                    std::cout << "Valid choices range from 1 to 5.\n"
                                 "Please retry.\n";
                    choice = 0;
                }
                if(choice < 1 || 5 < choice) {
                    std::cout << "Valid choices range from 1 to 5.\n"
                                 "Please retry.\n";
                }
            } while(choice < 1 || 5 < choice);
            if(!players.at(i).moveOnMap(choice)) {
                std::cout << "Sorry, you can't go there. Please retry.\n";
                --i;
            }
        }
    } while(choice != 5);
}

You can use it this way:

main.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <limits>
#include "Game.h"

void waitForEnter();

int main()
{
    Game g;
    g.setBasicPlayGround(8, 12);
    g.addRocksToGround();
    g.addPlayersToGround();
    g.addPlayersToGround(1, '^');
    g.play();
    waitForEnter();
    return 0;   
}

void waitForEnter()
{
    std::cout << "\nPress ENTER to continue...\n";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Thanks for that, i'll look through it and analyze it. Is there any concise articles on what breaks encapsulation and how to properly make sure that encapsulation is maintained? and anything related to how classes work and stuff? The main problem for me is that i'm self taught, no ones ever showed me how to write code apart from these forums. All the tutorials and books I write show you how to write the code and give some explanations on what the code is doing and why its doing it but it really doesn't go deep into it. most people need to just be shown that and that usually good enough for them but with me I have to understand everything about something to see the whole picture, and when I do i completely get it, I need to know things that normally wouldn't matter so any kind of in depth articles that arent hundreds of pages long would be super helpful. also I was also wondering if there was a program that outputs the step by step process that your program takes when it executes so it starts in main and then say main calls a function the program would output that info. I think that would be helpful for my understanding as well. It would show me how my programs execute and call functions and the things that go on underneath, it would help me figure out variable initialization problems too. I know I could just output to the console using cout for stepping through functions but im sure theres other things I cant use cout for that would be helpful to see like memory instructions. but maybe im wrong.
Last edited on
Is there any concise articles on what breaks encapsulation and how to properly make sure that encapsulation is maintained?
I would recommend a book on OO design.

also I was also wondering if there was a program that outputs the step by step process that your program takes when it executes so it starts in main and then say main calls a function the program would output that info
Any C++ debugger will let you run your program step-by-step interactively. For example these are the default key bindings in the Visual Studio debugger:
F5 - Continue running until next breakpoint.
Shift+F5 - Terminate debuggee.
F10 - Run until next line/statement, ignoring any function calls in the middle (in standard debugging parlance, "step over"). Note: functions will still be executed, but the debugger will not stop in them unless there's a breakpoint.
F11 - Same as step over, but go into functions ("step in")
Shift+F11 - Continue running until current function returns ("step out").
The debugger also lets you set breakpoints, which instruct it to pause execution when the control flow passes through specified points in the program. There's even data breakpoints, which pause execution when specified memory regions are modified. These are trickier to use, though, and you can only set a few because they're implemented in hardware.

Besides being used to track down bugs, debugging is actually a common technique to understand how a program written by someone else works. Even veterans can better understand a program by seeing it run that by just reading it.
Last edited on
ok thanks. What books would you recommend for OO design?
Topic archived. No new replies allowed.
Pages: 12