Pointers vs references for class objects

Pages: 1234
AH i see, thanks!

I had another question. I want to learn how that kind of stuff works, where do you learn that from? I see people explaining how the code works and what its doing and why it's doing it and in my 10 years of learning on and off, no book or video taught me that stuff.

What I mean is that knowing what code is doing just by looking at it, I struggle with that for the most part too, I understand some stuff, but knowing HOW the code interacts with stuff is my main issue I guess is what i'm trying to say, because watching videos and reading books your just following along with the examples and copying what they do, so it's a very surface level understanding of the code because your not really being taught how the code works for the most part, sometimes you do but mainly it's just "Copy what I do" and then you just learn to build programs exactly the way you saw the tutor make it without really understanding it, see what I mean? hopefully that made sense.


EDIT:

This one:
1
2
3
4
int& enemy::GetEnemyHealth()
{
return m_health;
}

returns a reference. Not a simple number. A reference to the member variable. Anyone who has this reference can do whatever they like with the actual member variable. The reference is basically another name - an alias - for the actual member variable. So with that code, enemy.GetEnemyHealth() gives you a reference to the actual member variable. Effectively, enemy.GetEnemyHealth() IS the actual member variable.


This :
1
2
3
4
int enemy::GetEnemyHealth() // note the difference
{
return m_health;
}

does not return a reference. It returns just an int. A number. Whoever gets that has just a number, they do NOT have the actual member variable.


I see what you mean, yeah that makes sense. my recollection of references is coming back to me now. I should have checked my notes, it has a whole section on references and pointers.
Last edited on
@Ch1156, if you've not read Stroustrup's "Tour" of C++, or the "C++ Principles in Practice", I would think they should do the trick.

The "Tour" assumes programming knowledge, perhaps a professional in need of upgrading their knowledge from pre C++11 to modern C++ in a short, terse walk through.

The "Principles" is Stroustrup's text for teaching (though it doesn't read like a textbook, it stands alone for readers not attending). It does begin at an elementary level in the first few chapters, and even then you might find something that makes skimming those worth while. It then quickly moves into modern C++, ending somewhere in the intermediate domain.

Now, Scott Meyers books are highly recommended as "must reads".

Herb Sutter's, too. If you don't know these names, look them up.

Josuttis (sp?) is good for the STL/standard library tour and reference.

Other than that, one must have things things in their fingers. You have to write to get it in the way you wish.

I come from late 70's/80's, before C++. I was a C programmer for years, with plenty of time in various assemblers. I have found that viewpoint very advantageous as a programmer/engineer. It may not be required, but it does help me "see" things as you described.

One point about the "return a reference" in the case of GetEnemyHealth(). It removes the "private" status with a "getter". It "desolves" the private status, meaning it may as well have been a public variable.

However, the other point made with the example (e.GetEnemyHealth() -= e.Get...) - it's on the previous "page" here, so I don't see it - the point is the code decremented the m_health member because it returned a reference to the member.

My reaction to that is, why not make a "damage" function? Give damage how much to hit with, let it figure out how health should be impacted.


Last edited on
I'll definitely check those books out. When i'm wrtiting code I visualize whats happening and how its working and interacting, not sure if anyone else does that but thats how I usually work.

Hmm, I wonder if theres a programming language other than C out there that would help me understand programming better in general. I have tried writing in other languages, LUA, Python, C#, Java, but I always come back to C++, idk theres just something about C++ that resonates with me, but maybe it's just because I know it the best.

So I should just make those variables public instead of private? I do know when to make variables private and public for the most part but it seems to me that having access to those variables publically would be a bad idea. I can usually tell if a variable or function will only be doing work in the class and those are made private, but I just struggle with that because on one hand, I need access to them, but making them all public is pointless because the entire point of the class (other than organizing similar methods and variables) is to hide and encapsulate data, and making each variable have a getter and setter rins that too, which is what ended up happening in my code now anyways even though i tried hard to avoid it.

I can write a damage function and in attack I can setup a randomized switch that randomly chooses attacks and damages the player/enemies based on the number chosen.


EDIT 8/4 11:09 PM - I added the damage function and cleaned up the code a little bit, still needs more work but I added random attacks, not sure if i should have made all the class objects in that function though, its just a kludge for now until i figure something out. Havent done anything for the player yet.

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <stdlib.h>

using namespace std;

enum weapVarNames
{
    WEAPON_NAME = 1,
    WEAPON_AMMO_TYPE = 2,
    WEAPON_DAMAGE_DEALT = 3,
    WEAPON_COST = 4,
    ALL_INFO = 5
};

class Enemy;
class Player;

class Weapon
{
    public:
        Weapon(const string m_weaponName, const string m_weaponAmmoType,
               const float m_weaponDamageDealt, const float m_weaponCost): weaponName(m_weaponName),
                                                                            weaponAmmoType(m_weaponAmmoType),
                                                                            weaponDamageDealt(m_weaponDamageDealt),
                                                                            weaponCost(m_weaponCost){}
        void DisplayWeaponInfo(int select);

        float GetWeaponDamage() const { return weaponDamageDealt; }

        ~Weapon();

    private:
        const string weaponName;
        const string weaponAmmoType;
        const float weaponDamageDealt;
        const float weaponCost;
};

Weapon::~Weapon()
{

}

void Weapon::DisplayWeaponInfo(int select)
{
    switch(select)
    {
        case WEAPON_NAME:
            cout << weaponName;
            break;
        case WEAPON_AMMO_TYPE:
            cout << weaponName << " Ammo Type: " << weaponAmmoType;
            break;
        case WEAPON_COST:
            cout << weaponName << " Cost: $" << weaponCost;
            break;
        case WEAPON_DAMAGE_DEALT:
            cout << weaponName << " Damage: " << weaponDamageDealt;
            break;
        case ALL_INFO:
            cout << weaponName << endl;
            cout << weaponName << " Ammo Type: " << weaponAmmoType;
            cout << weaponName << " Cost: $" << weaponCost;
            cout << weaponName << " Damage: " << weaponDamageDealt;
            break;
        default:
            cout << "Could not display requested information" << endl;
    }
}

class Shop
{
    public:
        void Merchant();
        void ProcessTransaction();


    private:
};

class Enemy
{
    public:
        Enemy(float m_enemyHealth): enemyHealth(m_enemyHealth){}
        ~Enemy();

        void Attack();
        void Damage(Weapon& weapon, Player& player);

        float GetEnemyHealth() const { return enemyHealth; }
        void SetEnemyHealth(float m_enemyHealth){ enemyHealth = m_enemyHealth; }

    private:
        float enemyHealth;
};

Enemy::~Enemy()
{

}

class Player
{
    public:
        Player(float m_playerHealth): playerHealth(m_playerHealth){}
        ~Player();

        void Attack(Weapon& weapon, Enemy& enemy);

        int Killed() const
        {
            if(playerHealth <= 0)
                cout << "Player is dead! Game over." << endl;

            return 0;
        }

        float GetPlayerHealth() const { return playerHealth; }
        void SetPlayerHealth(float m_playerHealth){ playerHealth = m_playerHealth; }

    private:
        float playerHealth;
};

void Player::Attack(Weapon& weapon, Enemy& enemy)
{
    enemy.SetEnemyHealth(enemy.GetEnemyHealth() - weapon.GetWeaponDamage());
}

enum attackType
{
    BITE = 0,
    CLAWS = 1
};

void Enemy::Attack()
{
    srand(time(NULL));

    int randEnemyAttack = rand() % 3;

    Enemy raptor    (100);
    Weapon claws    ("Claw Attack", "N/A", 100, 0);
    Weapon bite     ("Bite", "N/A", 120, 0);
    Player player   (100);

    switch(randEnemyAttack)
    {
        case BITE:
            cout << "Raptor uses "; bite.DisplayWeaponInfo(WEAPON_NAME); cout << "!" << endl;
            raptor.Damage(bite, player);

            cout << "Player Health: " << player.GetPlayerHealth() << endl;
            player.Killed();
            break;
        case CLAWS:
            cout << "Raptor uses "; claws.DisplayWeaponInfo(WEAPON_NAME); cout << "!" << endl;
            raptor.Damage(claws, player);

            cout << "Player Health: " << player.GetPlayerHealth() << endl;
            player.Killed();
            break;
        default:
            cout << "Raptors Attack Missed!!!" << endl;
    }
}

void Enemy::Damage(Weapon& weapon, Player& player)
{
    player.SetPlayerHealth(player.GetPlayerHealth() - weapon.GetWeaponDamage());
}

Player::~Player()
{

}

int main()
{
    //Create Weapons
    //Weapon handgun  ("Handgun", "9mm", 5.0, 300.0);
    //Weapon rifle    ("Rifle", "5.56mm", 50, 600);

    //Setup enemy and player

    Enemy  raptor(100);
    Player player(100);

    raptor.Attack();

    return 0;
}
Last edited on
When i'm wrtiting code I visualize whats happening and how its working and interacting, not sure if anyone else does that


No doubt, it's common. I see machines, sometimes electronic, sometimes mechanical, sometimes a mixture. Sometimes I see analogies (dealing a deck of cards for the operation of a stack, for example). I also will never forget the old Borland IDE's view of the CPU registers and the stack from back on the DOS days.

...theres just something about C++ that resonates with me...


It may be more than that. Most of the languages you list don't support RAII (or at least not as directly), and they took a lot from C++, but left so much behind.

Whenever I work in C# or Java, I feel like my office has been stuffed into a phone booth.

So I should just make those variables public instead of private?


I didn't mean to convey that. I was trying to convey that the return of a reference to a member may as well remove privacy, but that's not a suggestion to make them public.

There are times when such values should be public, like a simple C struct. You'll know because there's nothing to protect about them, and nothing beats a simple usage like p.x, p.y, etc.

...on one hand, I need access to them...


When member data represents what is genuinely internal, the need to access that data from outside a member function hints either a member function should be created to do that work, or something else in the design may be incorrect that brings that situation about.

Sometimes a class should be interior to a class, a private class. Such a class could be all public, but since it is otherwise private to the owning class, no one else can use it or get to it.

Sometimes classes should be friends to each other, but not frequently. When objects naturally work together as a family of components, perhaps making them friends (usually in one direction, very seldom both directions) makes sense.

The most important thing in such cases is that it shouldn't be a user feature.

because the entire point of the class (other than organizing similar methods and variables) is to hide and encapsulate data


While encapsulation is generally the case, sometimes they're just structs.

The real points about classes may yet to be well documented. For example, if the point is to encapsulate, then what is the point of encapsulation? The answer may seem obvious, but sometimes things just need to be quite open. For example

1
2
3
4
struct cmp
{
 bool operator()( int a, int b) {.....}
};


This is an older paradigm. We use lambdas for this now, but for a long while this was the primary way to make a comparison function (and it could be a template class). This is a class (just spelled struct). There is nothing to encapsulate, but the technique makes the instance usable like a function.

Sometimes the point of a class is to create a type more than anything else. One of the main features of the language (and many like it) is the ability to select a method by type. There may not be as much to encapsulate about it (like cmp above), but the type is informative on its own, when the occasion arises.

Sorry, I haven't studied your code just yet...and I need to change gears (I've been working on my car, blew a timing belt). I might check on it again later.


There are times when such values should be public, like a simple C struct. You'll know because there's nothing to protect about them, and nothing beats a simple usage like p.x, p.y, etc.


It's difficult to know when stuff should be public, but it's much easier for me to understand when something should be private. Like processing a transaction between shop and player, thats something that should just happen when you buy something so the code for that should be hidden i think.
It's difficult to know when stuff should be public


It helps if, when designing your code, you think first and foremost about what you want to interface to your class to be. What services do you want that class to supply to the rest of the program? What do you want other code to be able to request from the class? What information will the class need to be given by other code?

Considering those questions will give you at least some of the answers to the question "what should be public?"

Yeah that's definitely something i'm going to have to work on. It's just going to take a lot of practice and peer review.

btw, when writing my constructors, do the values have to be set to anything? I cant remember and I dont think its setting the values to anything in the Player.cpp File. I set the stuff up in main but thats setting up the actual player and weapon, idk.

Player.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
29
30
31
32
33
34
35
36
#ifndef PLAYER_H_INCLUDED
#define PLAYER_H_INCLUDED

#include "Weapon.h"
#include "Player.h"
#include "Enemy.h"

class Enemy;

class Player
{
    public:
        Player
        (
            float m_playerHealth,
            float m_playerExperience,
            float m_playerMoney
        );

        ~Player();

        void Attack(Weapon& weapon, Enemy& enemy);
        void GiveExperience();

        int Killed() const;

        float GetPlayerHealth() const { return playerHealth; }
        void SetPlayerHealth(float m_playerHealth){ playerHealth = m_playerHealth; }

    private:
        float playerHealth;
        float playerExperience;
        float playerMoney;
};

#endif // PLAYER_H_INCLUDED 



Do I still have to set a value to the variables in the body of the constructor?

Player.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
#include <iostream>

#include "Player.h"

using std::cout;
using std::endl;

Player::Player
        (
            float m_playerHealth,
            float m_playerExperience,
            float m_playerMoney

        ) : playerHealth(m_playerHealth),
            playerExperience(m_playerExperience),
            playerMoney(m_playerMoney){}

Player::~Player()
{

}

void Player::Attack(Weapon& weapon, Enemy& enemy)
{
    enemy.SetEnemyHealth(enemy.GetEnemyHealth() - weapon.WeaponDamage());
}

int Player::Killed() const
{
    if(playerHealth <= 0)
        cout << "Player is dead! Game over man, game over." << endl;

    return 0;
}
Last edited on
For "Player", you should read up on the rule(s) of 0, 3, 5...

Player has no written default constructor, and no indication that it should be deleted or made private. As such, the compiler may generate a default constructor if any code required one. Say:

Player p;

This would cause a default construction of p. It can happen if you use some containers, especially maps and sets, without you really thinking about it.

The compiler built default constructor won't apply any initialization to members. They'll be left uninitialized.

Either deny default construction, by making it a deleted constructor, or make one which default initializes the members, or set default values in the class (depending on the C++ version you're set to compile for).

You could, for example:


1
2
3
4
5
6
7
8
9
10
11
class Player
{
    public:

// all that other stuff as is....

    private:
        float playerHealth { 100.0f };
        float playerExperience { 0.0f };
        float playerMoney { 10.0f };
};


This basically says to the compiler, start these off this way if nothing else. Take care on the compiler version relative to initialization syntax forms.

In the old days, we would have to write a default constructor to do this.

With respect to Enemy::attack:

What strikes me foremost is that functions called for player (player.killed() for example), apply to that local instance of player.

That instance evaporates when the function concludes, so I don't know what value there is in killing or attacking a player that evaporates in the time it takes to run the function.


//How can I use an object so I can use other class functions and stuff in other classes
//without re instantiating them?


Like you did in Enemy::Damage (player is a parameter).

Of course, now you're asking, where do I get a player to use when calling Enemy::Attack?

I can't see the rest of the code, so I don't know where you are storing players, but a container of players is a likely start.

The rest flows from the same idea.


Last edited on
For "Player", you should read up on the rule(s) of 0, 3, 5...


I'll check that out.

Player has no written default constructor


Default constructor? I thought as long as there was one constructor it was ok? I'm not sure I really understand.

What strikes me foremost is that functions called for player (player.killed() for example), apply to that local instance of player.


Do you mean this?:

1
2
3
4
5
6
7
int Player::Killed() const
{
    if(playerHealth <= 0)
        cout << "Player is dead! Game over man, game over." << endl;

    return 0;
}


This basically says to the compiler, start these off this way if nothing else. Take care on the compiler version relative to initialization syntax forms.

In the old days, we would have to write a default constructor to do this.


Yeah I completely forgot about using the braces for variable initialization, I fixed that in my current code. Speaking of which, here is the code in its entirety, unfinished in most places of course:

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
24
#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <stdlib.h>

#include "Player.h"
#include "Enemy.h"
#include "Weapon.h"
#include "Shop.h"

using std::cout;
using std::endl;

int main()
{
    Enemy  raptor(100, 20);
    Weapon claws("Claws", "Claw", 15, 0, 0);
    Player player(100, 0.0, 200);

    raptor.Attack(raptor, claws, player);

    return 0;
}


Player.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
29
30
31
32
33
34
35
36
#ifndef PLAYER_H_INCLUDED
#define PLAYER_H_INCLUDED

#include "Weapon.h"
#include "Player.h"
#include "Enemy.h"

class Enemy;

class Player
{
    public:
        Player
        (
            float m_playerHealth,
            float m_playerExperience,
            float m_playerMoney
        );

        ~Player();

        void Attack(Weapon& weapon, Enemy& enemy);
        void GiveExperience();

        int Killed() const;

        float GetPlayerHealth() const { return playerHealth; }
        void SetPlayerHealth(float m_playerHealth){ playerHealth = m_playerHealth; }

    private:
        float playerHealth{100.0};
        float playerExperience{0.0};
        float playerMoney{300.0};
};

#endif // PLAYER_H_INCLUDED 


Player.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
#include <iostream>

#include "Player.h"

using std::cout;
using std::endl;

Player::Player
        (
            float m_playerHealth,
            float m_playerExperience,
            float m_playerMoney

        ) : playerHealth(m_playerHealth),
            playerExperience(m_playerExperience),
            playerMoney(m_playerMoney){}

Player::~Player()
{

}

void Player::Attack(Weapon& weapon, Enemy& enemy)
{
    enemy.SetEnemyHealth(enemy.GetEnemyHealth() - weapon.WeaponDamage());
}

int Player::Killed() const
{
    if(playerHealth <= 0)
        cout << "Player is dead! Game over man, game over." << endl;

    return 0;
}


Enemy.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
29
30
31
32
33
#ifndef ENEMY_H_INCLUDED
#define ENEMY_H_INCLUDED

#include "Weapon.h"
#include "Enemy.h"
#include "Player.h"

class Player;

class Enemy
{
    public:
        Enemy
        (
            float m_enemyHealth,
            float m_experienceGiven
        );

        ~Enemy();

        void Attack(Enemy& enemy, Weapon& weapon, Player& player) const;
        void Damage(Weapon& weapon, Player& player);
        void Killed() const;

        float GetEnemyHealth() const { return enemyHealth; }
        void SetEnemyHealth(float m_enemyHealth){ enemyHealth = m_enemyHealth; }

    private:
        float enemyHealth{100.0};
        float experienceGiven{10.0}; //The experience this enemy gives the player when defeated
};

#endif // ENEMY_H_INCLUDED 


Enemy.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
#include <iostream>
#include <time.h>
#include <stdlib.h>

#include "Enemy.h"
#include "Enums.h"

using std::cout;
using std::endl;

Enemy::Enemy
(
    float m_enemyHealth,
    float m_experienceGiven

) : enemyHealth(m_enemyHealth),
    experienceGiven(m_experienceGiven){}

Enemy::~Enemy()
{

}

void Enemy::Killed() const
{
    if(enemyHealth <= 0)
        cout << "Enemy is dead!" << endl;
}

void Enemy::Attack(Enemy& enemy, Weapon& weapon, Player& player) const
{
    srand(time(NULL));

    int randEnemyAttack = rand() % 3;

    switch(randEnemyAttack)
    {
        case BITE:
            cout << "Enemy attacks with "; weapon.DisplayWeaponInfo(WEAPON_NAME); cout << "!" << endl;
            enemy.Damage(weapon, player);

            cout << "Player Health: " << player.GetPlayerHealth() << endl;
            player.Killed();
            break;
        case CLAWS:
            cout << "Enemy attacks with "; weapon.DisplayWeaponInfo(WEAPON_NAME); cout << "!" << endl;
            enemy.Damage(weapon, player);

            cout << "Player Health: " << player.GetPlayerHealth() << endl;
            player.Killed();
            break;
        default:
            cout << "Enemies Attack Missed!!!" << endl;
    }
}

void Enemy::Damage(Weapon& weapon, Player& player)
{
    player.SetPlayerHealth(player.GetPlayerHealth() - weapon.WeaponDamage());
}


Weapon.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
29
30
31
32
33
34
35
36
37
#ifndef WEAPON_H_INCLUDED
#define WEAPON_H_INCLUDED

#include <iostream>

using std::string;

class Weapon
{
    public:
        Weapon
        (
            const string    m_weaponName,
            const string    m_weaponAmmoType,
            const float     m_weaponDamageDealt,
                  int       m_weaponMagazineSize,
                  int       m_ammoReserveSizeMax
        );


        void    DisplayWeaponInfo(int select);

        void    Reload();
        float   WeaponDamage() const;

        ~Weapon();

    private:
        const string    weaponName{"GenericWeapon"};
        const string    weaponAmmoType{"GenericAmmo"};
        const float     weaponDamageDealt{10.0};
              int       weaponMagazineSize{1};
              int       ammoReserveSizeMax{10};

};

#endif // WEAPON_H_INCLUDED 


Weapon.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
#include <iostream>

#include "Weapon.h"
#include "Enums.h"

using std::cout;
using std::endl;

Weapon::Weapon
(
    const string    m_weaponName,
    const string    m_weaponAmmoType,
    const float     m_weaponDamageDealt,
          int       m_weaponMagazineSize,
          int       m_ammoReserveSizeMax

) : weaponName(m_weaponName),
    weaponAmmoType(m_weaponAmmoType),
    weaponDamageDealt(m_weaponDamageDealt),
    weaponMagazineSize(m_weaponMagazineSize),
    ammoReserveSizeMax(m_ammoReserveSizeMax){}

Weapon::~Weapon()
{

}

float Weapon::WeaponDamage() const
{
    return weaponDamageDealt;
}

void Weapon::DisplayWeaponInfo(int select)
{
    switch(select)
    {
        case WEAPON_NAME:
            cout << weaponName;
            break;
        case WEAPON_AMMO_TYPE:
            cout << weaponName << " Ammo Type: " << weaponAmmoType;
            break;
        case WEAPON_DAMAGE_DEALT:
            cout << weaponName << " Damage: " << weaponDamageDealt;
            break;
        case ALL_INFO:
            cout << weaponName << endl;
            cout << weaponName << " Ammo Type: " << weaponAmmoType;
            cout << weaponName << " Damage: " << weaponDamageDealt;
            break;
        default:
            cout << "Could not display requested information" << endl;
    }
}


Enums.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef ENUMS_H_INCLUDED
#define ENUMS_H_INCLUDED

enum weapVarNames
{
    WEAPON_NAME = 1,
    WEAPON_AMMO_TYPE = 2,
    WEAPON_DAMAGE_DEALT = 3,
    ALL_INFO = 4
};

enum attackType
{
    //Enemy
    BITE = 0,
    CLAWS = 1
};

#endif // ENUMS_H_INCLUDED 



I omitted shop class because im running out of characters and it isnt used anywhere else in the code yet so it's irrelevant right now.
Requoting myself ;)



With respect to Enemy::attack:

What strikes me foremost is that functions called for player (player.killed() for example), apply to that local instance of player.



I mean in the function Enemy::attack()


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
void Enemy::Attack()
{
    srand(time(NULL));

    int randEnemyAttack = rand() % 3;

    Enemy raptor    (100);
    Weapon claws    ("Claw Attack", "N/A", 100, 0);
    Weapon bite     ("Bite", "N/A", 120, 0);
    Player player   (100);


    switch(randEnemyAttack)
    {
        case BITE:
            cout << "Raptor uses "; bite.DisplayWeaponInfo(WEAPON_NAME); cout << "!" << endl;
            raptor.Damage(bite, player);

            cout << "Player Health: " << player.GetPlayerHealth() << endl;
            player.Killed(); // ******************************************************************<< HERE ...and similar
            break;
        case CLAWS:
            cout << "Raptor uses "; claws.DisplayWeaponInfo(WEAPON_NAME); cout << "!" << endl;
            raptor.Damage(claws, player);

            cout << "Player Health: " << player.GetPlayerHealth() << endl;
            player.Killed();
            break;
        default:
            cout << "Raptors Attack Missed!!!" << endl;
    }
}


Now, you've changed attack in your more recent post, but the question you were asking with the version above was about these things.

In this example, player is limited to this function, so whatever happens to that player evaporates at the end of this function.





Default constructor? I thought as long as there was one constructor it was ok? I'm not sure I really understand.


Your Player constructor takes three floats. As long as all construction of a Player is given those 3 floats, there isn't a problem, but if:

Player p;

The compiler creates it's own default constructor. The members would be uninitialized.

This relates to a question now several posts above, I've lost track where.

The point being that default constructors can come up in contexts you are not necessarily expecting.

You asked something about initialization of the Player (your current constructor is ok, no need to re-assign in the body), but since you haven't dealt with the default constructor that brings up one of those "gotcha things" about C++, of which the rule(s) of 0, 3 and 5 address.

The "gotcha" point 1 is about what happens if you don't expect a default constructor to be called, but since the default constructor has not been explicated to be denied, the compiler generates one - sometimes to horrible effect.

Another from the rule(s) of 0, 3 & 5 are copy constructors. They, too, come up without really being aware, but in your example the compiler generated version would be fine (always is when there's just POD data, like a collection of floats and such). When you use a STL container to hold these objects, there is an automatic implication that they'll be copied. If, for some reason, some members don't do well with default copy, you have to write something to fix that - or deny the copy.

All of that can also impact assignment operators, because the compiler can generate defaults for you.

Read the rule(s) of 0, 3 and 5...you'll figure that part out.
Last edited on
AH, yeah I was mulling it over and it didnt make any sense because i was creating a whole new player and enemy and weapons, now i should be able to plug any weapon and enemy into the function adn get the desired results.
@Ch1156, I notice your reply above was 10 minutes before I added material to my post about default constructors and related material.
Oh sorry I missed your extended response!

Your Player constructor takes three floats. As long as all construction of a Player is given those 3 floats, there isn't a problem


if i try to do Player p; my compiler throws errors, it refuses to let me compile, not sure if that's standard or not, I recently turned on a bunch of compiler flags for errors and stuff in its settings (I use Code Blocks btw).

Unless I create a new Player(); in the class and a new constructor Player::Player(){}, then it will. Maybe that's what you mean though, I havent yet used a copy constructor or even overloaded a constructor before, thats something i need to delve into though.

You asked something about initialization of the Player


I cant remember what I was referencing but if it was the re-initialization of player and other class objects in my attack function, I solved that just by putting a reference to them in the functions argument list.

I do plan on using a vector at some point, maybe an array too so thats something I should try to learn now. I'll research those rules of 0, 3 and 5 too.


Do you know if theres a program or a way to see whats going on with the code in the background? Like for example lets say I have a string, and i assign it something, the program would visually show or represent in some way that string assigned to that value, another one is a pointer and when you put something on the heap, it will visually represent it and show you whats going on behind the scenes, I think visually representing what the code is doing would be a massive help to me but I dont know if anything like that even exists, as it is now it's really really difficult to know what the code does just by someone trying to explain it to me, i'm a visual learner so i have to see it in pictures or examples to really comprehend it in a way thats completely understandable, if not maybe i can hire someone to make something like that. as a learning tool.

It doesnt need to have pictures but something showing the instructions the code is doing might be helpful.
Last edited on
Your Player constructor takes three floats. As long as all construction of a Player is given those 3 floats, there isn't a problem, but if:
Player p;

The compiler creates it's own default constructor. The members would be uninitialized.


if i try to do Player p; my compiler throws errors, it refuses to let me compile, not sure if that's standard or not

IF class has custom constructor, THEN compiler does not create default constructor.
1
2
3
4
// This class does not have default constructor:
class Custom {
  Custom( int );
};

But you can override that and force the compiler to create:
1
2
3
4
class Custom {
  Custom() = default; // Compiler implements this the default way.
  Custom( int );
};



Do you know if theres a program or a way to see whats going on with the code in the background? Like for example lets say I have a string, and i assign it something, the program would visually show or represent in some way that string assigned to that value, another one is a pointer ....


Are you not familiar with debuggers and "watch" or "inspection" windows?

It depends on the IDE as to what debug "views" are available, but some have fairly good tools to watch the code operate "from inside".

I find using a debugger is helpful just to read code as it operates (especially the way some people write).
Are you not familiar with debuggers and "watch" or "inspection" windows?


I'm not. At least I wasnt. I watched a few videos on it and it seems super useful, I have the watches window up which shows all the variables and what they're initialized too. I wish I would have know how to use that feature a long time ago, would have saved me a ton of grief. being able to follow the code as it executes is super helpful.
yes, yes it is. And you can do a poor-mans version of it with simple print statements, if you are ever stuck without your tools. (someday you may find yourself on some remote unix server that has g++ but not much else installed, and some small task you need to get done fast, etc).

you can exploit watches on systems where they are not tracked well across function calls. You can make a global pointer to the variable and watch that, rather than watch each local reference to it by name.

debugging efficiently is a critical skill... work on it, its as important as coding itself.
It's a game changer.

That and unit testing.

If you have a disassembly window (or whatever your IDE calls is), you can see the machine code generated by your C (though in most debug modes it will be unoptimized).

In most debuggers, you can change the values you see in order to test certain assumptions about code behavior.

There is usually two "step" commands, step into and step over. This controls how the debugger treats function calls. Step into traces into a function. Step over calls the function but moves to the line after the function call.

There's often a "step out" - which runs until the current function returns. It's very useful when you end up tracing into code you really didn't need to trace.

Learn to set breakpoints - code stops on them to show specific points of interest.

Use the "divide and conquer" approach when required. Some bug may happen where it's so deep into nested function calls that you can't really tell where the error is happening. So, set a break point before the error and another after. Trace with steps, then every now and then when you know things have worked up to that point, set another breakpoint so that when, inevitably, you loose track of where you are, you can restart the debug and just "run" until you reach that last "breadcrumb" breakpoint you left behind.

There are SOME IDE's which even offer reverse execution....stepping the program backwards so you can "re-try" something you know went wrong, but didn't really "see".

Some offer "data" and "conditional" breakpoints. A conditional breakpoint can be set to fire when a variable reaches a certain value, like "break on the 100th loop".

A "data" breakpoint can be made to fire when some piece of data changes, without being attached to any line of code.

In my experience, going back decades, the debugger is the one thing that brings me back to Visual Studio. For years, back in the late 80's and 90's, Borland had one of the best debuggers, but then Microsoft finally "caught up" somewhere around Visual C++ 5 or 6 (somewhere in the late 90's).

Welcome to a brand new world!

Oh, look up valgrind. For Visual Studio there is Visual Leak Detector. There are several such tools - they check memory issues above and beyond (and in conjunction with) the debugger. Sometimes the most mysterious bugs are memory issues, and the problem may happen long before you see an error or crash. These tools find those.
Last edited on
Thanks for the advice, I should start using visual studio honestly, Code Blocks is nice and i've used it since i started 10 years ago but VS is more professional. I've been watching videos on how to debug in VS.


Now I had a quick question about something in my project. is there a way to pass multiple class objects into a function without a vector? or does a vector/array have to be used? I have a bunch of class objects for enemy made, which are the enemies and I was wondering if there's a way to do that but perhaps not. I passed with a vector and my code is working like i want it to, here is my main.cpp file to illustrate:

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
#include "pch.h"

#include <iostream>
#include <string>
#include <vector>
#include <time.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>

#include "Player.h"
#include "Enemy.h"
#include "Weapon.h"
#include "Shop.h"

using namespace std;

void Arena(Player& player, vector<Enemy> enemyHolder);
void ChooseRandomEnemy(vector<Enemy> enemyHolder);

int main()
{
	Player player(100, 0.0, 200);
	Shop shop(40);
	Enemy raptor(100, 20, "Raptor");
	Enemy trex(500, 50, "T-Rex");
	Enemy triceratops(350, 40, "Triceratops");
	Enemy spinosaurus(420, 56, "Spinosaurus");

	vector<Enemy> enemyHolder;

	enemyHolder.push_back(raptor);
	enemyHolder.push_back(trex);
	enemyHolder.push_back(triceratops);
	enemyHolder.push_back(spinosaurus);

	std::cout << "Welcome to Dinosaur Arena" << std::endl;
	std::cout << "Please make a selection" << std::endl;

	int choice{};

	std::cout << "1. Go to the Shop" << std::endl;
	std::cout << "2. Go to the Arena" << std::endl;
	std::cout << "3. View Stats" << std::endl;
	std::cin >> choice;

	switch (choice)
	{
		case 1:
			shop.Buy(player);
			break;
		case 2:
			Arena(player, enemyHolder);
			break;
		case 3:
			//Function  to show player stats
			break;
		default:
			std::cout << "You have made an incorrect selection" << std::endl;
	}

	return 0;
}


void Arena(Player& player, vector<Enemy> enemyHolder)
{
	Weapon scratch("Scratch", "NIL", 6, 0, 0);
	Weapon bite("Bite", "NIL", 9, 0, 0);
	Weapon stomp("Stomp", "NIL", 13, 0, 0);

	std::cout << "Get ready to battle!" << std::endl;

	ChooseRandomEnemy(enemyHolder);
}

void ChooseRandomEnemy(vector<Enemy> enemyHolder)
{
	srand(time(NULL));

	int randEnemy = rand() % enemyHolder.size();

	std::cout << "Your enemy is the ";

	std::cout << enemyHolder[randEnemy].EnemyName();
}
When passing a vector by value as you're doing with Arena and ChooseRandomEnemy, the vector is copied. The function is working on a copy, a deep copy. Every Enemy in the vector is a duplicate. If the function were to modify an Enemy in the vector, the code that calls this would never see it.

Pass by reference instead. This doesn't create a copy. Technically it may be represented as a pointer, but not always because optimization works magic on this.

void Arena( Player & player, vector< Enemy > & enemyHolder );

Pages: 1234