You got a few nice answers already, but the huge elephant in the room in your question is this one:
heard from someone that using inheritance must be avoided, and we should use interfaces instead
As a rule of thumb, when somebody gives you a rule of thumb, then ignore it. This not only goes for "somebody telling you something", but also for reading stuff on the internet. Unless you know why (and can really stand behind it), such advice is worthless and often very harmful.
In my experience, the most important, and helpful concepts in OOP are "low coupling" and "high cohesion" (classes/objects know as little as possible about each other, and each unit is responsible for as few things as possible).
Low Coupling
This means that any "bundle of stuff" in your code should depend on its surroundings as little as possible. This goes for classes (class design) but also objects (actual implementation), "files" in general (i.e., number of #include
s per single .cpp
file, number of import
per single .java
file and so on).
A sign that two entities are coupled is that one of them will break (or need to be changed) when the other is changed in any way.
Inheritance increases coupling, obviously; changing the base class changes all subclasses.
Interfaces reduce coupling: by defining a clear, method-based contract, you can change anything about both sides of the interface freely, as long as you don't change the contract. (Note that "interface" is a general concept, the Java interface
or C++ abstract classes are just implementation details).
High Cohesion
This means to have each class, object, file etc. be concerned with or responsible for as little as possible. I.e., avoid large classes that do a lot of stuff. In your example, if your weapons have completely separate aspects (ammo, firing behaviour, graphical representation, inventory representation etc.), then you can have different classes that represent exactly one of those things. The main weapon class then transforms into a "holder" of those details; a weapon object then is little more than a few pointers to those details.
In this example, you would make sure that your class representing the "Firing Behaviour" knows as little as humanly possible about the main weapon class. Optimally, nothing at all. This would, for example, mean, that you could give "Firing Behaviour" to any object in your world (turrets, volcanoes, NPCs ...) by just a snap of a finger. If you at some point in time want to change how weapons are represented in the inventory, then you can simply do so - only your inventory class knows about that at all.
A sign that an entity is not cohesive is if it grows larger and larger, branching out in several directions at the same time.
Inheritance as you describe it decreases cohesion - your weapon classes are, at the end of the day, big chunks who handle all kinds of different, non-related aspects of your weapons.
Interfaces indirectly increase cohesion by clearly splitting off responsibilities between the two sides of the interface.
What to do now
There still are no hard and fast rules, all of this is just guidelines. In general, as user TKK mentioned in his answer, inheritance is taught a lot in school and books; it is the fancy stuff about OOP. Interfaces are both probably more boring to teach, and also (if you go past trivial examples) a bit harder, opening up the field of dependency injection, which is not so clear-cut as inheritance.
At the end of the day, your inheritance-based scheme is still better than having no clear OOP design at all. So feel free to stick with it. If you wish to, you can ruminate/google a bit about Low Coupling, High Cohesion and see if you wish to add that kind of thinking to your arsenal. You can always refactor to try that out if you wish to, later; or try out interface-based approaches on your next larger new module of code.