Creating different weapons in class

Pages: 1234
Exercise 3

This is basically the same as before.

Start with JLBorges code from earlier. http://www.cplusplus.com/forum/beginner/240805/2/#msg1072055

Separate the files into Pistol.hpp, Pistol.cpp and main.cpp

Put everything into a namespace Choose a good name, investigate namespace alias.

Implement (write code for) the restriction on the maximum number of bullets to 15. Use a constexpr qualified variable to store this value in main, and use the name of the variable so that you don't have magic numbers in the code. Look at line 9 in JLBorges code, also mbozzi had some code to check if a value is inside a range. Print an error message if it isn't.

Modify the fire function so that it takes an argument for the number of bullets to fire in one burst. Make sure the message to say "Weapon Empty" works properly even when the number of bullets to fire is greater than what is left before firing .

Do the same to the load function.
Exercise 4

This is a side exercise, aimed at investigating and getting you to think about what happens with signed and unsigned integers.

You need to do your own research here.

Can you think of any problems that arise from using these types? Think about the range of values these types can hold, look them up, tell us what they are. Write some code to print them out. What can happen around the value of 0 ? Also around the maximum and minimum values these types can hold?

What are the potential problems when you have functions like Add and Subtract. Write some overloaded versions of these functions that take arguments each combination of the different types, and print the answer. Just use int and unsigned int for now.

Try using these functions with different values around zero, max, min. You should get some results that are logically incorrect. Why might this be happening ?

What conclusions can you state about doing arithmetic with a mixture of signed and unsigned types?

How are the values for different types stored in binary? There is more than one way. How does the compiler do the signed part?

Investigate how to print integers in decimal, octal, hexadecimal and binary. The last one is trickier than the others, but still has an easy solution.

Exercise 5

Start with the completed code from exercise 3.

Implement the semi-automatic and automatic firing of the pistol. Make it work with the existing fire function that takes the number rounds to fire in one burst.

Alter how it displays the output, so auto looks like this, depending on how many rounds to fire:

Auto mode
Bang Bang Bang Bang Bang 


While semi-auto looks like this:

Semi-auto mode
Bang 
Bang 
Bang 
Bang 
Bang 
Last edited on
Exercise 6

This where it gets a little more complicated.

Derive 3 classes from the Pistol base class. Name them Baretta354, FiftyCal, TwentyTwo. The Baretta is 0.354 inch caliber.

We would like to have virtual polymorphism, so the Pistol class is going to have pure virtual functions. These will be the interface for the other 3 classes. Remember to include a virtual destructor.

Use the keyword override in the derived classes when overriding the functions.

To get different behavior (as trivial as it is), each fire function is to print something different: "Boom" for the FiftyCal ; "Bang" for the Baretta; "Crack" for the TwentyTwo.

Now we are in a position to store some data about our pistols. Name two obvious member variables to have. Which class will they go in? Note we are not going to have any protected member variables.

In addition, each pistol is going to have different capacity for the number of rounds it will hold. FiftyCal 10 ; Baretta 15 ; TwentyTwo 20. We are no longer allow the user to specify how many rounds the pistol will hold, but they can specify how rounds to load. What does that mean for how you declare your classes?

Make sure that all the constructors work.

Test the loading and firing of each pistol. When loading, if there are more rounds than what the pistol will hold, print a message saying pistol is full, and how many spare rounds there are. If zero rounds are loaded print a message saying so. Make the tests comprehensive, they should still work given input of: negative, zero, less than capacity, and greater than capacity.
Exercise 7

Now we are going to separate the Interface of the Pistol class.

Create a class named PistolInterFace or IPistol.

Put all the virtual function declarations into it.

Make Pistol class inherit IPistol.

Now to do some polymorphism:

Create a std::vector of std::unique_ptr to Pistol, put some pistols in to it. Use emplace_back to construct them in place.

Use a range base for loop to iterate through the pistols , testing the loading and firing of them. Consider using auto&& , investigate why that is a good thing to use.

Exercise 8

The project is getting bigger now .....

Create a class named Magazine.

What member variables will it have? Create an interface of functions, including the IMagazine class.

Now create 3 derived classes, one for each type of Pistol class you have. You could abbreviate the names of these a little, as in FiftyCalMag. So this is quite similar to what you did for the Pistol class.

How does this affect what we had with the 4 Pistol classes? There are several things going on.
Exercise 9

We are now going to have a Player class.

Imagine this is a game where things are lying around, ready to be picked up. We are not going to have a game map or anything fancy yet.

What I would like is to allow a Player to have a certain number of "Things". Maybe the program can randomly produce an item, the player has a choice if they want to acquire it.

Also to be able to fit the correct magazine to a Pistol, produce an error if it is the wrong type
Exercise 10

In games like Tomb-raider, one does not find a fully working weapon, they have to search for several components.

A pistol could be broken up into it's components: The pistol itself, the firing mechanism, the magazine. One could make it even harder by requiring the user to find the correct rounds to be loaded into a magazine as well.

Ok, I believe I got everything for #2, I got the bounds checking and the error throwing, I also broke everything up into separate files. If I missed anything let me know. I was looking at your post from the second page, although glancing at #3 it seems to have some stuff from #3 in there as well:

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "Pistol.h"

int main()
{
    constexpr int roundsLimit = 15;

    Pistol pistol(roundsLimit);

    pistol.Fire(11, roundsLimit);

    return 0;
}



pistol.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
38
39
40
41
#ifndef PISTOL_HPP_INCLUDED
#define PISTOL_HPP_INCLUDED

#ifndef PISTOL_H_INCLUDED
#define PISTOL_H_INCLUDED

class Pistol
{
    public:
        Pistol() = default;
        explicit Pistol(int magCap) : m_magazineCapacity(magCap){}

        void Fire(int fireAmount, int roundsLimit)
        {
            if(m_magazineCapacity < 0 || fireAmount > m_magazineCapacity)
            {
                throw std::invalid_argument("Error, magazine capacity too low or fire amount greater than magazine capacity of weapon.");
            }
            else
            {
                for(int i = 0; i < fireAmount; i++)
                {
                    std::cout << "Bang\n";
                    m_magazineCapacity--;
                    std::cout << "Rounds left in magazine: " << m_magazineCapacity << "\n";
                    if(m_magazineCapacity == 0)
                    {
                        std::cout << "Magazine empty\n";
                    }
                }
            }
        }

    private:
        int m_magazineCapacity = 3;
};

#endif // PISTOL_H_INCLUDED


#endif // PISTOL_HPP_INCLUDED 


I'll work on the other ones. for #3 I looked at JLBorges code and am a bit confused on what aspects to replicate, you said line 9, do you meant the ternary operator? I know of it but have never used it in my 10 years of programming but it seems like it really helps shorten code up a bit, should it be used whenever possible or only in certain circumstances like in JLBorges code?, I've never even seen constexpr until this thread, hopefully i'm using it right.
Last edited on
Put everything into a namespace Choose a good name, investigate namespace alias.


Should a new file be created for the namespace? I've used namespaces before so i'm familiar with their functionality and how to implement them, but should it go into its own file or the pistol.hpp?
bump
Should a new file be created for the namespace?
No.

You can use the same namespace multiple times. Actually the use of namespaces is to put things in context.

E.g. You may have a namespace weapon where you put all class and functions that has to do with weapon. To access an element within a namespace would look like this:
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
#ifndef PISTOL_HPP_INCLUDED
#define PISTOL_HPP_INCLUDED

#ifndef PISTOL_H_INCLUDED
#define PISTOL_H_INCLUDED

namespace weapon
{
  class Pistol
  {
  ...
  };
}

//---


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

int main()
{
    constexpr int roundsLimit = 15;

    weapon::Pistol pistol(roundsLimit);

    pistol.Fire(11, roundsLimit);

    return 0;
}
thanks, I figured that was it. I don't really see the point of writing all the code in the exercises. I appreciate the help, I just don't understand how to use virtual and inheritance properly, I think just bare functions with pseudo code could be more helpful as I have spent more time writing functions than I even have solving my problem.

I guess the main problem is when writing a superclass, what functions to put in the children classes, the actual structuring of them all, because lets say I have a super-class Weapon that encompasses all weapon types and it has a sub class, Rifles, what goes in that class in terms of functionality? and that rifles class has children classes AK47 and M4, what goes in those? if the superclass has the functions for Fire() and Load() what else? I guess ammo types and a selector switch to determine if it can go full auto or not. Also with virtual and pure virtual functions, how would those be used in that 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
#include <iostream>


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

        void Fire();
        void Reload();

    private:
        std::string weaponName = {};
        enum ammoType {_556mm, _9mm, _45mm};
        enum fireRate {FULLAUTO, SEMIAUTO, BURST};
        int magazineSize = {};
};

class Rifle : public Weapon
{

};

class AK47 : public Rifle, Weapon
{

};

int main()
{

    return 0;
}
Last edited on
just google any terms you're fuzzy on and hopefully you'll find some nice article explaining them. If that article uses some unfamiliar terms, google those, too.
Initial google https://www.geeksforgeeks.org/virtual-function-cpp/
More direct answer on why "virtual" specifically https://stackoverflow.com/questions/2391679/why-do-we-need-virtual-functions-in-c


virtual allows for run-time polymorphism, which is especially useful when resolving ambiguity -- suppose you had a function that took a Weapon reference and you wanted it to resolve to the correct Fire() method. 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
#include <iostream>

using namespace std;

class Weapon
{
public:
    /*virtual*/ void Fire() const { cout << "Weapon::Fire\n"; }
};

class Rifle : public Weapon
{
public:
    void Fire() const { cout << "Rifle::Fire\n"; }
};

void CauseDischarge(const Weapon& weapon)
{
    weapon.Fire();
}

int main() 
{
    Weapon weapon;
    weapon.Fire();  // Weapon::Fire

    Rifle rifle;
    rifle.Fire();  // Rifle::Fire

    CauseDischarge(weapon); // Weapon::Fire
    CauseDischarge(rifle);  // Weapon::Fire if line 8 doesnt have 'virtual'

    return 0;
}


can test at https://repl.it/@icy_1/WhirlwindRapidBellsandwhistles
Then for pure virtual function, done by adding =0; to the end of the method, you are specifying
(1) this method must be implemented in the children
(2) this base class is abstract (can't create direct instances of this class)

To clearly show that a child is implementing something that also exists in the parent, it's a good idea to use the 'override' keyword in the child classes. There will be a convenient compilation error if a child uses 'override' for a method in which the parent does not mark it 'virtual'.
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
#include <iostream>

using namespace std;

class Weapon
{
public:
    virtual void Fire() const = 0;  // A pure virtual function; children must implement
    virtual void Name() { cout << "Weapon\n"; }  // Default if children don't implement
};

class Rifle : public Weapon
{
public:
    void Fire() const override { cout << "Rifle::Fire\n"; }
};

class AK47 : public Weapon
{
public:
    void Fire() const override { cout << "AK47::Fire\n"; }
    void Name() override { cout << "AK47\n"; }
};

void CauseDischarge(const Weapon& weapon)
{
    weapon.Fire();
}

int main() 
{
    // No longer allowed because Weapon is considered an abstract class
    //Weapon weapon;

    Rifle rifle;
    AK47 ak47;

    CauseDischarge(rifle); // Rifle::Fire
    CauseDischarge(ak47);  // AK47::Fire
    rifle.Name();  // Weapon, since Rifle does nto have its own implementation
    ak47.Name();   // AK47

    return 0;
}


https://repl.it/@icy_1/StandardAgitatedTransformations
Last edited on
So in layman's terms a virtual function just makes it so you can redefine the same function in a child class to have different implementation from the one in the base class, and the one in the base class is basically just a default template in case the child class function is not called, and this is to prevent you from having to re-type the same code in the other functions, right?

And pure virtual forces you to define it in the child class instead of the base class, with virtual you can still define a default function with some code that can be used by default but with pure virtual you cant do that and instead have to define it in the child class, correct?
Last edited on
So in layman's terms a virtual function just makes it so you can redefine the same function in a child class to have different implementation from the one in the base class


No. You can redefine functions that are not virtual.
Last edited on
So what would be a Layman explanation as to what virtual and pure virtual do? I was reading up on it on some websites but I still don't get it, they way they explain it doesn't make sense to me, i'm just not grasping what they're trying to get across.

I'm having trouble understanding exactly what it's purpose is, icy1 says:

virtual allows for run-time polymorphism, which is especially useful when resolving ambiguity


Is that the only use of it? What does this mean exactly?
Last edited on
If you have a pointer-to-base, and you call a function through that pointer-to-base, and the function is virtual, you actually call the most-derived function of the actual object it's actually pointing to, rather than the base one.

Run this.

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>

class Base
{
    public:
    virtual void speak()
    {
        std::cout << "I am base!\n";
    }
};

class Derived : public Base
{
    public:    
    void speak()
    {
        std::cout << "I am derived!\n";
    }
};

int main()
{
    Base baseObject;
    baseObject.speak();
    
    Derived derivedObject;
    derivedObject.speak();
    
    Base* pointer_to_base = &baseObject;
    pointer_to_base->speak();
    
    pointer_to_base = &derivedObject;
    pointer_to_base->speak(); 
}


See that final function call? It called Derived::speak(), because the function speak is virtual.

If you then change the function so that speak isn't virtual, that final call will call Base::speak()

Last edited on
Ok so I think I understand, but what is "Most Derived" what makes it most derived? what if you had 4 classes that derive from base? would they all call their own speak functions? and if base Speak wasnt virtual they would then just call the base Speak function right?
Pages: 1234