I want to try practice a few things that I have learned by making a very small simple console based game. I was going to make 3 characters, player, enemy and enemy boss, and was going to have them inherit from entity. So I thought I should have entity be an abstract class, but is it normal for an abstract class to have all of its functions and variables virtual? This would be about 7 or 8 functions, and 3 or 4 member variables? From what I have found, most abstract classes only have 1 or so functions virtual, but I do not know if this is just because it is tutorial code I am looking at and therefore it is being kept simple.
EDIT : I have just read that virtual member variables are not a thing, so it would just be virtual member functions then in the abstract class. Not sure how I would handle the private variables
is it normal for an abstract class to have all of its functions [...] virtual?
Yes, there's nothing unusual about such a design. Furthermore, a class without any data members that only contains pure virtual member functions is called an "interface".
Not sure how I would handle the private variables
Since the class doesn't contain any code, there would be no way to access private members if you were to declare any. There's nothing wrong with declaring such members, although it would be a bit strange. In classes intended to be inherited from, it's much more common for non-public members to be declared protected, rather than private.
#include <iostream>
#include <string>
#define TEST_ENTITY
struct entity
{
// defaulted constructors are not virtual; they can't be virtual
// defaulted assignment operators are usually not virtual
// rule of five defaults: "When a base class is intended for polymorphic use,
// its destructor may have to be declared public and virtual.
// This blocks implicit moves (and deprecates implicit copies),
// and so the special member functions have to be declared as defaulted"
// see: https://en.cppreference.com/w/cpp/language/rule_of_three#Rule_of_zero
entity() = default ;
entity( const entity& ) = default ;
entity( entity&& ) noexcept = default ;
entity& operator= ( const entity& ) = default ;
entity& operator= ( entity&& ) noexcept = default ;
virtual ~entity() = default ; // defaulted virtual destructor
// all other functions are pure virtual
virtual std::string name() const = 0 ;
virtualvoid name( std::string ) = 0 ;
// note that a virtual function can be private
// and it can still be overridden in a derived class
private: virtualvoid helper() = 0 ;
// just for testing helper: a public function to call the private virtual function helper()
#ifdef TEST_ENTITY
public: void call_helper()
{
std::cout << "calling helper() on object with name '" << name() << "' -> " ;
helper() ;
}
#endif // TEST_ENTITY
};
int main()
{
struct player : entity
{
explicit player( std::string name ) : _name( std::move(name) ) {}
std::string _name ;
virtual std::string name() const override { return _name ; }
virtualvoid name( std::string new_name ) override { _name = std::move(new_name) ; }
private: virtualvoid helper() override { std::cout << "overridden main::entity::helper()\n" ; }
};
player p { "a_player" } ;
entity& e = p ;
e.call_helper() ;
}