vishal:
Somebody help I think the error says I have something from the GameObject class that has not been over-riden in the child classes But am not sure please help Maybe I have to do public inheritance
Marioalexsan: Did you provide definitions for your GameObject virtual functions in a source file? Either that, or declare them as pure with
virtual sf::Vector2f getPosition() const = 0;if you don't need it in the base class.
Marioalexsan: Defining stuff for Paddle doesn't matter
vishal: You mean GameObjects.cpp? no. I only have GameObjects.hpp I wanted to make it abstract
Dbug: If it's a "pure" virtual base class, you need the = 0
vishal: ok, @mario
Dbug: Also on a pure style point of view, I generally recommend to keep the same order of stuff between the various derived levels, here you have "update" followed by "get/set" in the base class, and the opposite in the derived class. It makes it easier to navigate in the code later :)
vishal: @mario Thank you! Working I thought virtual meant that it was intended to be over written I don't know C++ that well
Dbug: optionally
vishal: Oh.
Dbug: it's not uncommon for a derived virtual implementation to call the parent implementation to do things like "specialization" for a specific class but still call some generic behavior common to all the classes
vishal: Ok, I'll fix that
Dbug: When you put the =0 you basically tell the compiler that there is no default implementation, it
vishal: @degub OH
Dbug: that it's mandatory for every derived class to implement it
Marioalexsan: And that makes the class abstract, which means you can't instantiate a GameObject directly. You'll need to create Paddles and Balls and whatnot.
vishal: @dbug Can this happen if the parent function is not virtual?
Marioalexsan: You can call the base class methods if you want, but it's up to you in both cases.
vishal: so what's the use of virtual? If normal functions in parent can be overwritten?
Dbug: If a method is not virtual, it has to be implemented, either in the CPP or inline in the class declaration
vishal: @dbug oh ok makes sense
Marioalexsan: The difference between normal methods and virtual methods is that virtual methods are called based on the object's type at runtime, while normal methods are called with whatever the type of the pointer is.
vishal: Hmm..
Dolphy: @vishal Also you can "override" methods that are not marked as virtual. (I put quotation marks because you do that without using the override keyword). Virtual methods are stored in something called vtable and they allow you to use runtime polymorphism.
vishal: ok @dolpy
Marioalexsan: The typical case of virtual is to allow you to manage "GameObject" pointers and call something like "draw" or "do something" which will do different things depending of the actual type of object. The caller does not care, the calle is the one knowin it was a "ball" or a "paddle"
vishal: Any resource were I can learn this
Dbug: It's generic OOP concepts, all the core stuff is common to C++, Java, etc... implementation details may be different, syntax as well, but the concepts are generic
vishal: All makes sense now: Normal functions have to have function definitions Virtual functions the definition is optional In both cases over-riding can happen Abstract classes have virtual function = 0 ok @dbug thanks
Dbug: One thing to remember is that using a large inheritance graph for game elements is generally not working, using it for base functionalities like displaying, position, updates, etc... is fine
vishal: "ments is generally not working," you mean slow?
Dbug: Many early C++ games made the mistake of having an object graph with "friends" vs "ennemies" with "flying enemies" and "ennemies with guns", etc...
Marioalexsan: Dbug, try not to overcomplicate stuff right now.
Dbug: And then you want to have a "flying ennemy with gun" or a "friend that can fly" and things stop making sense Not overcomplicating, just saying: Don't even try to do that
vishal: ok thanks everyone
Dbug: One level inheritance, like what you have "interface inheritance" is fine and easy to manage
Marioalexsan: @vishal
#include <iostream>
struct BaseVirtual {
virtual void say() { std::cout << "BaseVirtual" << std::endl; }
};
struct DerivedVirtual : public BaseVirtual {
virtual void say() { std::cout << "DerivedVirtual" << std::endl; }
};
struct Base {
void say() { std::cout << "Base" << std::endl; }
};
struct Derived : public Base {
void say() { std::cout << "Derived" << std::endl; }
};
int main()
{
BaseVirtual baseVirtual;
DerivedVirtual derivedVirtual;
Base base;
Derived derived;
BaseVirtual* vPtr;
vPtr = &baseVirtual;
vPtr->say();
vPtr = &derivedVirtual;
vPtr->say();
Base* ptr;
ptr = &base;
ptr->say();
ptr = &derived;
ptr->say();
}This outputs
BaseVirtual
DerivedVirtual
Base
Base
Dbug: If you find yourself wanting to add one more level of derived, it's probably going to bite you later, so stop and think it :)
vishal: @mario hmm reading
Marioalexsan: For virtual methods, C++ can call the actual method of the runtime object. For non-virtual methods, it can only call the method it can see through the pointer's type. So assigning a Derived to a Base and calling say() will not call Derived's say method.
vishal: Ok so virtual classes are to do things dynamically at run time
Marioalexsan: Pretty much. It's similar to Java (where everything is implicitly virtual) and C# (where you have virtual and abstract).
vishal: but, why doesn't ptr = &derived; ptr->say(); give error ptr was type Base
Marioalexsan: Derived is a subclass to Base, so you can do the assignment
vishal: Hm.. @mario still a little confusing why would someone not allow ptr->Say() to say "Derived" ptr's original type was Base*
Marioalexsan: C++ can't tell what method of which subclass to call if the method is not virtual.
vishal: so if this saying "Derived" is not allowed, the ptr = &Derived should give error while compiling
Marioalexsan: A Base* could be a pointer to any derived object, like Base, Derived, Derived2, Derived3, DerivedFromDerived, etc.
vishal: hm..
Marioalexsan: And the way to make C++ be able to call the relevant method from the relevant subclass is to make the method virtual, which means C++ will create a virtual table to store the data it needs at runtime.
vishal: What is the use case of having a parent pointer point to something to child
Marioalexsan: You can have an array which holds pointers to entities, with the base entity class having virtual void update() = 0; that is overridden in derived entities. That array can hold pointers to Player, Goblin, DynamicWater, Dragon, LevelCompleteTrigger, etc. It doesn't matter what the runtime types of the entities will be, you'll be able to go through the array and update the entities using update(). Without virtual you wouldn't be able to do that.
dolphy: That's how some game engines like Unity work
vishal: hmmm... nice, but what is the use of non-virtual ptr pointing to child Nice!
Marioalexsan: In some cases you only work with stuff from the base class and don't really care what the derived class adds. For example you could take a Transformable* as an input to a function and do some generic transformations on it. It won't matter if it's an actual Transformable or some class that derived from it.
vishal: I am thinking Thanks anyway
Dbug: Just to give some other ideas of what you can do with inheritance, most often used in serious/tools code and less often in actual game code, is that you can derive from multiple base classes to add specific functionalities to your classes. In one of the tools I maintained, we had a class hierarchy of objects like what you have with your GameObject->Paddle and GameObject->Ball But on top of that, our objects would also derive from interfaces classes for specific things like "undo/redo" system, "load/save" serialization, etc...
Marioalexsan: I'm not a huge fan of multiple inheritance of stuff that's not interfaces.
Dbug: Totally, you risk the Diamond Effect, that's why we had a main base class, and zero to n pure interfaces classes just adding a few additional methods
Marioalexsan: That's pretty much the same as C# / Java
Dbug: The main reason why we rarely use that in games but mostly in tools, it's because it tends to be costly because you often need to dynamic_cast all over to check if something implements a specific interface.
Marioalexsan: Ehh The costs of virtual stuff probably pales in comparison to something like one DB call anyway.
Purple Prince Bambo: Not at the scale you'd do it in games where performance is critical Also db calls are often done async anyway.
Dbug: Sure, but this type of thinking is also why the Unreal Engine requires so much memory and CPU to do things engines 10+ years older would have no trouble doing at a lower cost :p
Purple Prince Bambo: And it's an irrelevant factor if the game doesn't call to the db The overhead of the dynamic cast is one it is likely to incur and want to avoid
Dbug: "death by a thousand cuts" type of thinking
Purple Prince Bambo: Amen to that dbug
Marioalexsan: 🤷 That is true too
Dbug: Well, time to work a bit on my game, see ya
Marioalexsan: Game? Better add procedural generation, survival mechanics and an overdetailed RPG skill tree
EOF

