Pointers vs references for class objects

Pages: 1234
So in a class using virtual, I see some examples use pointers like so:

1
2
3
4
5
6
    Weapon *w = new Weapon;
    Bomb *b = new Bomb;
    Gun *g = new Gun;
    w->loadFeatures();
    b->loadFeatures();
    g->loadFeatures();


But i've been told that this is better or more modern:

1
2
3
4
5
6
7
8
9
10
11
    Bomb b;
    Gun g;
    Weapon& w = b;
    Weapon& w2 = g;
    Weapon w3;

    w.loadFeatures();
    w2.loadFeatures();
    w3.loadFeatures();

    return 0;


Full Code

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
#include <iostream>
using namespace std;

class Weapon
{
    public:
        virtual ~Weapon() = default;

        virtual void loadFeatures()
        {
            cout << "Loading weapon features.\n";
        }
};

class Bomb : public Weapon
{
    public:
        virtual void loadFeatures() override
            {
                Weapon::loadFeatures();
                cout << "Loading bomb features.\n";
            }
};

class Gun : public Weapon
{
    public:
        virtual void loadFeatures() override
            {
                Weapon::loadFeatures();
                cout << "Loading gun features.\n";
            }
};

class Loader
{
    public:
        void Features(Weapon& weapon)
        {
            weapon.loadFeatures();
        }
};


int main()
{
    Bomb b;
    Gun g;
    Weapon& w = b;
    Weapon& w2 = g;
    Weapon w3;

    w.loadFeatures();
    w2.loadFeatures();

    return 0;
}
Last edited on
But i've been told that this is better or more modern:
That's nonsense. Instead of creating an artificial reference you can use the objects b and g right away.

I guess you missunderstood something.

True is that you should avoid raw pointer. Use smart pointer instead.
When you have objects it is a good idea to pass them as [const] reference.
I suspect that what might have happened here is the idea that "it's generally better to use a reference than a pointer" is what was intended, but as is often the case, whatever example was used to illustrate the syntax was simplified to the point that the benefits of doing so became lost; in this case, the objects are created locally, so neither a reference or a pointer was needed.

I think it's fairly uncontentious to say it is generally preferred to use a reference than a pointer, generally preferred to use smart pointers than raw pointers, generally preferred not to need them at all (but there are many situations where we do need them, of course).
@Repeater
Yes, the example is probably not the best here. Maybe I did not get the entire point.
I have definitely seen this sort of thing - create an object on the heap, use it and delete it, for zero benefit and added risk - in real code, though!

1
2
3
4
5
6
7
8
9
void function()
{
  // Seen this in real code. There was nothing preventing this going on the stack :(
  object* p_obj = new object;
  
  // do things with the object

  delete p_obj;
}

Suffice to say, this (and thus the original code examples above) is bad, as Coder777 says.
Last edited on
Hmm, yeah I was wondering about that, didnt seem to be necessary but I see it in tutorials and videos all the time. here is my updated code, should be much better.

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
#include <iostream>
using namespace std;

class Weapon
{
    public:
        virtual ~Weapon() = default;

        virtual void ReloadWeapon()
        {
            cout << "Reloading Weapon.\n";
        }

        virtual void FireWeapon()
        {
            cout << "Weapon Firing" << endl;
        }
};

class Bomb : public Weapon
{
    public:
        virtual void ReloadWeapon() override
        {
            cout << "Bomb Arming" << endl;
        }

        virtual void FireWeapon() override
        {
            cout << "Bomb Exploded" << endl;
        }
};

class Gun : public Weapon
{
    public:
        virtual void ReloadWeapon() override
        {
            cout << "Gun Reloading" << endl;
        }

        virtual void FireWeapon() override
        {
            cout << "Gun Firing" << endl;
        }
};


int main()
{
    Bomb bomb;
    Gun handgun;

    bomb.FireWeapon();
    bomb.ReloadWeapon();

    cout << "" << endl;

    handgun.FireWeapon();
    handgun.ReloadWeapon();

    return 0;
}


My understanding of virtual and pure virtual is better, I understand how binding and late binding work and apply to virtual. I know that pure virtual is just used as an interface when you don't want to define a function in the base class but want the inheriting classes to define it instead.

I'm more of a visual thinker and need to visualize how something works in order to understand it, and With this, I imagine one of those old time phone switchboards that a person needed to plug in to the right socket to get the call to the right person, in this case it's the program selecting the right function to use on the fly. Thats how i imaging late binding and virtual working, whereas a regular class with no virtual methods i imagine as a modern day phone system where it just goes straight to the method thats being called.

If my code is wrong somehow please correct me.
Last edited on
You had (unused) "Loader". You still can have one to demonstrate virtuals in action:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void handle( Weapon& w )
{
  w.FireWeapon();
  w.ReloadWeapon();
}

int main()
{
    Bomb bomb;
    Gun handgun;

    handle( bomb );
    std::cout << '\n';
    handle( handgun );
}

Sidenote: to arm an already exploded bomb is a remarkable feat ...


How would you write a function that returns either gun or bomb?
Thanks! Yeah I was getting a little confused with that, so I deleted it, I copied this code from a tutorial and have modified it for my own needs. Here is the updated code.

Oh also when the bomb is arming, it's arming a new bomb :)

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
#include <iostream>
using namespace std;

class Weapon
{
    public:
        virtual ~Weapon() = default;

        virtual void ReloadWeapon()
        {
            cout << "Reloading Weapon.\n";
        }

        virtual void FireWeapon()
        {
            cout << "Weapon Firing" << endl;
        }
};

class Bomb : public Weapon
{
    public:
        virtual void ReloadWeapon() override
        {
            cout << "Arming New Bomb" << endl;
        }

        virtual void FireWeapon() override
        {
            cout << "Bomb Exploded" << endl;
        }
};

class Gun : public Weapon
{
    public:
        virtual void ReloadWeapon() override
        {
            cout << "Gun Reloading" << endl;
        }

        virtual void FireWeapon() override
        {
            cout << "Gun Firing" << endl;
        }
};

void Handle(Weapon& weapon)
{
    weapon.FireWeapon();
    weapon.ReloadWeapon();
}


int main()
{
    Bomb bomb;
    Gun handgun;
    
    Handle(bomb);

    cout << "" << endl;
    
    Handle(handgun);

    return 0;
}
So I have a question. I want to add a knife weapon and other melee weapons, like a sword, axe etc, now obviously a knife cant fire a bullet, and doesnt have a magazine to be reloaded, my thought is I could add a stab function to weapon, but not all weapons stab so having guns and bombs inherit that seems like it shouldnt go in weapon since weapon should contain functions that are universal to all weapons.

I'm thinking of creating a melee weapon class and having all melee weapons inherit from that, but melee weapons are still weapons, so wouldnt they still be part of the weapon class? I guess I could still have the weapon class and have functions for weapon cost, durability etc that would apply to all weapons and then create a firearm, explosive and melee class that inherit from weapon, I could just have the three separate classes for each weapon category though if I really wanted, I mean I dont think I have to create that weapon class if I really didnt need it, but idk I'm thinking out loud here so you can kind of see the thought process I go through when i'm writing code.

I seem to really struggle with creating class hierarchy's like this more than anything.

any suggestions would be appreciated.
Last edited on
Although this discussion has already acknowledged the "example" or "contrived" nature of the code, I thought I'd point out a couple of things about virtual objects.

One of the primary use cases is to be able to store various objects in a container, which itself only knows the type of the base.

It wouldn't be possible to store guns or bombs in this:

std::vector< Weapon > w;

When a base like Weapon has pure virtual functions, this wouldn't even compile. Otherwise, if it does compile (because there are no pure virtual functions), the container can't store bombs or guns, only a base Weapon instance.

This could, however:

std::vector< Weapon * > w;

Being a pointer, the instantiated class could be a bomb or a gun without issue.

Unfortunately the vector would not delete those objects, as they are held by a raw pointer.

It is possible to use:

std::vector< std::unique_ptr< Weapon >> w;

However, this imposes some limitations (sometimes very good ones). You must "move" objects using unique_ptr, the pointers can't copy ( look into std::move ). You can't cast unique_ptr (though you can cast the 'get()' from a unique_ptr).

If that proves too restrictive, this is possible:

std::vector< std::shared_ptr< Weapon >> w;

This is a bit slower, but more flexible - and you can "hand over" copies of the pointers without ownership concerns. Sometimes this is the better choice, but it is better when you use the features, not just because you want the option "just in case".

My point here is that while references are being used in these examples, and that works, invariably the use of virtual functions in real world code or more advanced student work (which happens soon from this stage), you'll likely be dealing with pointers to these objects (in some form - hopefully use smart pointers).


So I have a question. I want to add a knife weapon and other melee weapons, like a sword, axe etc, now obviously a knife cant fire a bullet, and doesnt have a magazine to be reloaded, my thought is I could add a stab function to weapon, but not all weapons stab so having guns and bombs inherit that seems like it shouldnt go in weapon since weapon should contain functions that are universal to all weapons.


There are many, many ways to do this.
you could make an attack class that has types of attack (melee group, ballistics group, explosives/aoe group, whatever) and work more or less as-is. You can also do this as a switch inside the existing code (put a type on weapons, and if type==blah do whatever logics deeper inside). You can make full bore classes for each sub-type of weapon.

The approach needs to match the design which needs to satisfy the requirements, and if unsure of the requirements, you do the most open-ended thing that will satisfy the most complicated potential requirement, and if even that is unknown (why are you coding...) then you have to take the most generic approach.

The attack class looks best to me here -- weapon class has-a attack class, perhaps. Has-a works because all weapons have an attack and can call its methods, they just vary in behavior based on the variable states.

Last edited on
An attack class is a really good idea, I think i'll do that. I seem to struggle more with design that the code itself. Thanks!
don't sweat that. Design is harder than coding if you are average or better at coding.
any idiot can slap out code. Making large OOP designs work correctly without painting yourself into a corner and having a do-over takes practice, hard work, and know-how that comes with time and experience.

The say it aloud debugging technique is great for understanding here too.
reading code? Why did the author make this class that inherits that class which inherits another ... find the purpose of each one. It could be wrong (its about 50/50 or worse out in the world, people start making classes and can't seem to stop!), and just be bloated design (bad!!!). But try to give the benefit of doubt and understand their madness, see if you can find the elegance or utility of their design.
Also consider not bothering with a class hierarchy at all. The different weapons could be distinguished from properties alone. And if you read the property values from a file they will be easy to tweak.

And the "attack" method should be part of the character class, since it's the character that does things. A weapon is an inanimate object with properties that will determine what actually happens with the attack.
true. I was thinking in RPG words where 'attack' is a group of properties, not a verb. That is critical though; when making designs the difference between verbs and nouns is extremely important to how you lay it out.
Also consider not bothering with a class hierarchy at all. The different weapons could be distinguished from properties alone. And if you read the property values from a file they will be easy to tweak.


Thats another thing I struggle with, I mean you would think having different guns would call for inheritance but I could just make a weapon class and put all that in there. Lets say I do that, at what point is multi-class inheritance necessary?
@Ch1156,

I mean you would think having different guns would call for inheritance


One of the problems in both teaching and early study is that most examples use trivial ideas to illustrate both inheritance and virtual functions, without clear engineering as to why or, as importantly, why not.

Even before inheritance is taught, what I see in classroom study examples looks contrived just to illustrate how to make a class or struct, without much regard as to reason. The typical student example of a customer record class, for example, is unlikely to be found in real world, professional work. Database design (usually from SQL or related engines) changes on a whim, so it makes more sense to use containers which accept whatever a query from the database provides, not a fixed set of "fields" in a struct.

You may not regard what you're making as a class assignment, but I sense you are at a crossroads between student example and a more real project, where these design considerations evolve into something more like products and professional work.

Consider what happens with such weaponry when a game engine is used. In one popular engine I'm familiar with an indie game developer would create the artwork, fashion nodes representing the weapon's model, instantiate them as the player(s) acquire them (where the player is likely represented by a class). In that workflow, the weapon isn't really a class from which various definitions arise. It is a generic game node representing the position in the world upon which all manner of definitions from physics to special visual effects are applied to impact behavior. From a C++ viewpoint, this would look like a "node" class, with a transform (position and attitude), which then uses a simple container (perhaps a set or a map, could be a vector) to hold all of the "attributes" which flesh out what the weapon is. This container holds the attachment which define physics behavior, special visual effects, sounds, and...user code. This is not the layout of a virtual object as found in C++, but of a basic transform with attributes and behaviors attached to it.

One of the central notions in OOP is the selection of method by type. Overloaded functions are an elementary example:

1
2
double f( const double & );
int f( const int & );


Selection by "blind " type implies, perhaps, a virtual function, where code must access a generic interface, and manipulate that interface without knowledge of the specific type. This is illustrated in class examples with that typical "shape/circle/rectangle" family (with shape being the virtual base). Yet, you'd never find an actual product doing that (say CAD or Blender).

I fear what schools use as early learning examples tend to leave students with the impression that those are fleshed out strategies, but they are "toy" examples that don't fit the real world.

One example of a bad fit is in rendering engines. I've seen a number of rendering engines implement the rendering object as a virtual object. The virtual base is a render interface, and the derived class implements the OpenGL/DirectX/Vulkan/Metal API selected for whatever platform is running.

The problem is that the user does not really need to switch between these API's at runtime, so the virtual base design of the render interface is inappropriate. It may be "nice" to switch from OpenGL and DirectX when on Windows, or between OpenGL and Metal on Apple, but why would the user want to do that at runtime? Every virtual function has a minor penalty.

It would be better to implement by some other means which does not involved virtual functions, like a policy based template design, or simple "PIMPL" pattern where the implementation is selected at compile time.

So, when you have seriously well grounded reasons, fundamental reasons you can't avoid, which imply virtual base classes, use them. Otherwise, your thought may be a hint that it's not the only solution, and may not be the best solution.

Fitting attributes to a non-virtual object may be more efficient. Attaching objects representing behaviors may work better.

In games, in particular, you may discover you have several non-virtual base class designs, each with containers of attachments. Some of these may be collections of sound effects, visual effects, physics effects, and some of those might be virtual object designs. It is doubtful one needs a virtual base for a sound effect, for example, but of physics effects or special visual effects it is more likely.

From another perspective, contemplate what it takes to script or "mod" a game. What if the user were able to fashion new types of weapons? That couldn't be supported where each new type is a C++ class. That has to be implemented with runtime selectable attributes.



Last edited on
YES, all the examples i've seen on classes, inheritance and virtual are like that and not real world examples. That stuff really rubs me the wrong way because they're teaching me something that isnt how you would make it in the real world in a real programming situation most of the time, almost every book, youtube video and website i've ever visited is guilty of it.

I like to look at the source code for Doom 3 BFG edition, since game design is my only goal with learning C++. It's quite nice to look at and even though I don't understand it most of the time, the designs and how they implement code and name things and how they structure stuff in general has stuck with me and I often look at it for ideas and solutions, I mean take this for example from the weapon.h class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void					Think();
	void					Raise();
	void					PutAway();
	void					Reload();
	void					LowerWeapon();
	void					RaiseWeapon();
	void					HideWeapon();
	void					ShowWeapon();
	void					HideWorldModel();
	void					ShowWorldModel();
	void					OwnerDied();
	void					BeginAttack();
	void					EndAttack();
	bool					IsReady() const;
	bool					IsReloading() const;
	bool					IsHolstered() const;
	bool					ShowCrosshair() const;
	idEntity *				DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died );
	bool					CanDrop() const;
	void					WeaponStolen();
	void					ForceAmmoInClip();


The way the functions are named is really good, it's very clear what each does and they are concise and I rarely see getter and setters in this code, which is another design thing I struggle with. I seem to rely on getters and setters but thats not good design. I rewrote the weapon class I made earlier in my first post here and this is what I have:

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
#include <iostream>
#include <string>

using namespace std;

class Weapon
{
    public:
        Weapon();
        ~Weapon();
        
        void SetWeaponName(string m_name){ weaponName = m_name; }
        string DisplayWeaponName() const { return weaponName; }
        
        void SetWeaponDamage(int m_damage){ weaponDamageDealt = m_damage; }
        float DisplayWeaponDamage() const { return weaponDamageDealt; }
        
        void SetWeaponAmmoType(string m_ammoType){ weaponAmmoType = m_ammoType; }
        string DisplayWeaponAmmoType() const { return weaponAmmoType; }
        
        void WeaponSetup(string weapName, int weapDamage, string weapAmmo);
    
    private:
        string weaponName;
        string weaponAmmoType;
        float weaponDamageDealt;
        float weaponCost;
};

Weapon::Weapon()
{
    weaponName = "Default";
    weaponAmmoType = "DefaultAmmoType";
    weaponDamageDealt = 0.0;
    weaponCost = 1.0;
}

Weapon::~Weapon()
{
    
}

void Weapon::WeaponSetup(string weapName, int weapDamage, string weapAmmo)
{
    Weapon weapon;
    
    weapon.SetWeaponName(weapName);
    cout << "Weapon Name: " << weapon.DisplayWeaponName() << endl;
    
    weapon.SetWeaponDamage(weapDamage);
    cout << "Weapon Damage: " << weapon.DisplayWeaponDamage() << endl;
    
    weapon.SetWeaponAmmoType(weapAmmo);
    cout << "Weapon Ammo Type: " << weapon.DisplayWeaponAmmoType() << endl;
}

int main()
{
    Weapon weap;
    //Is this overwriting The other? Does a pointer need to be used.
    weap.WeaponSetup("Handgun", 5.0, "9mm");
    weap.WeaponSetup("Rifle", 30.6, "5.56mm");
    weap.WeaponSetup("Shotgun", 25.9, "Buckshot");

    return 0;
}


I'm not sure how to go about getting rid of the accessor methods and turning this into actually good code. I want to be able to create weapons for the player but I dont want global access to the variables.
Last edited on
Are you expecting the weapon name, damage, ammo type to change? If not, consider making them const variables that get set in a constructor init list, and don't allow a default constructor.
They wont change during gameplay if thats what you mean, basically I want to make a template that I can create any kind of weapon from.
Pages: 1234