Classes Part 2.
Requirements:
Linux Distribution
g++
any text editor
My Setup:
Debian 10
g++ version 6.3.0
pluma
Welcome back to the tutorials on classes! Today we will go over access
specifiers , and basic inheritance. Lets start off with inheritance.
Inheritance is a way to abstract out similar classes in a hierarchy. It allows
you to perform general functions that may act differently between objects of a
similar class. For example, there are multiple rendering libraries that can be
used for game development. These include OpenGL, Vulkan, DirectX (microsoft),
Metal (OSX), and Mantle(AMD). OpenGL and Vulkan are both crossplatform.
Game engines may have these abstracted out into specialized classes that inherit
from the same renderer class. Depending on the type of renderer, you may use
different specialized functions. To show a quick example:
class Renderer{ private: //private data that may appear in any specialized classes protected: //protected data that may appear in any specialized classes. public: Renderer(); // initialize any private or public data with default constructor // parameterized constructor with initialization ~Renderer(); // destructor virtual void draw() = 0; //this is a virtual function and needs to be //implemented in the specialized class }; class OpenGLRenderer : Renderer { private: //private data that may only appear in OpenGLRenderer protected: //protected data that may only appear in OpenGLRenderer public: OpenGLRenderer(); // OpenGLRenderer Default constructor // parameterized constructor with initialization ~OpenGLRenderer(); // OpenGLRenderer destructor void draw(); // implement with OpenGL Functions }; class DirectXRenderer : Renderer { private: //private data that may only appear in DirectXRenderer protected: //protected data that may only appear in DirectXRenderer public: DirectXRenderer(); // DirectXRenderer Default constructor // parameterized constructor with initialization ~DirectXRenderer(); // DirectXRenderer destructor void draw(); // implement with DirectX Functions };
In this example, the Renderer class acts as an abstraction to allow different
rendering API to work based on the one you use. So let me create a smaller
example that allows me to go a little more in-depth with it:
#include <iostream> class Value{ private: unsigned int m_size; //cant be accessed from derived class protected: std::string m_name; //can be accessed from derived class public: Value():m_size(sizeof(*this)),m_name("Value"){} Value(unsigned int size, std::string name): m_size(size),m_name(name){} ~Value(){} virtual void display() = 0; unsigned int getSize(){ return m_size;} unsigned int Size() const { return m_size;} }; class IntValue : public Value{ private: int m_val; public: IntValue(): Value(sizeof(int),"Int Value"), m_val(0){} IntValue(int val): Value(sizeof(int),"Int Value"), m_val(val){} IntValue(const IntValue & copy): Value(copy.Size(),copy.m_name),m_val(copy.m_val){} void display(){ std::cout << "Printing Int Value: " << m_val << std::endl; } }; class CharValue : public Value{ private: char m_val; public: CharValue(): Value(sizeof(int),"Char Value"), m_val(0){} CharValue(char val): Value(sizeof(int),"Char Value"), m_val(val){} CharValue(const CharValue & copy): Value(copy.Size(),copy.m_name),m_val(copy.m_val){} void display(){ std::cout << "Printing Char Value: " << m_val << std::endl; } }; int main(int argc, char *argv[]){ IntValue foo = IntValue(8675309); CharValue bar = CharValue('b'); Value * baz = new IntValue(335896); foo.display(); bar.display(); baz->display(); return 0; }
Will print out :
Printing Int Value: 8675309 Printing Char Value: b Printing Int Value: 335896
The relationship between a publicly derived class and its base class is “is
a”, meaning that it is a specialization of a more generic type, and as such, it
implements the behavior of that generic class and possibly more. The
relationship between a privately derived class and its base class is
“implemented in terms of”. It prevents objects from being considered extensions
of the base class. An example of privately-derived inheritance is a from a
a library called boost. It contains a class called boost::noncopyable that
prevents objects of a privately derived class from being copied. When deriving
from a protected base class, public and protected members of the base class
become protected members of the derived class. When it comes to access,
private members can only be accessed by objects of the same class, protected
can be accessed by objects of the same class OR objects of a derived class,
and public members can be accesses by any object or function. In my first
example, i had not specified an access specifier for the base classes like i did
with my other example. By default, base classes are private so:
class derived : base { }; is equal to class derived : private base { };
Also note in my second example, i used a pointer to the base type to create
a derived type. By marking the function “virtual”, I allowed the derived classes
specialized function be executed despite of the fact that Value::display() = 0