What is base class inheritance use for?

closed account (Ey80oG1T)
So let's say I have these files:

1
2
3
4
5
6
7
8
9
10
11
12
13
// dog.h file
class Dog
{
public:
  void function();
}

// cat.h file
class Cat
{
public:
  void function();
}


I was thinking of having a base class so I don't have to define the function 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
// animal.h file

class Animal
{
public:
  void function();
}

// dog.h file

class Dog
{
public:
  // nothing here
}

// dog.cpp file

void Dog::function()
{

}

// and same thing for cat 


However, the above doesn't seem to work. I check Stack Overflow: https://stackoverflow.com/questions/15117591/why-is-inherited-member-not-allowed, and it said the same thing. I turns out you have to define a function in the derived class as well! This is the same for virtual functions as well. So what's the purpose of having a base class?
Last edited on
what's the purpose of having a base class
Strictly speaking, you don't need them. C has gotten by without classes completely. :)

But the benefits of of having a base class could be to (1) avoid code duplication, and (2) to allow for run-time polymorphism via virtual functions. Also, there's the curiously recurring template pattern, https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern but I won't discuss that here since I've hardly ever used it, maybe someone else wants to.

(1) Let's say you have a Thing class that has {a, b, c} variables. ThingExtra class has {a, b, c, d} variables. Instead of creating two independent classes, you could just have,
1
2
class Thing { int a, b, c; };
class ThingExtra : public Thing { int d; };


(2) Have you used polymorphism in your studies yet? One of the biggest places you'll see inheritance used is in combination with run-time polymorphism.

Let's say, both Dog and Cat derive from Animal, and you have a container of Animal pointers.
You can iterate through the container and call a virtual function on the base-class pointer which then calls the corresponding sub-class function.
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
// Example program
#include <iostream>
using std::cout;

class Animal {
  public:
    virtual void hello() const =0;
};

class Dog : public Animal {
  public:
    void hello() const override
    {
        cout << "Woof!\n";   
    }
};

class Cat : public Animal {
  public:
    void hello() const override
    {
        cout << "Meow!\n";   
    }
};

#include <vector>
int main()
{
    Cat cat;
    Dog dog;
    
    std::vector<Animal*> animals { &cat, &dog };
    
    for (const auto& animal : animals)
    {
        animal->hello();   
    }
}

Meow!
Woof!


Run-time polymorphism determines (at run-time) that the first Animal pointer is pointing to a Cat, and the second Animal pointer is pointing to a Dog, and calls the corresponding overridden functions. Run-time polymorphism can also be done through references, although you can't directly store a reference in a container.
Last edited on
closed account (Ey80oG1T)
oh, I see. thanks! but besides from polymorphism and variables, a base class isn't useful for preventing code duplications from functions right?
If there is a function in the base class, then derived classes can all access that function without duplicating it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base {
  public:
    void increment()
    {
        n++;   
    }
    
  private:
    int n = 0;
};

class Derived : public Base { };

#include <vector>
int main()
{
    Base base;
    base.increment();
    
    Derived derived;
    derived.increment();
}
Last edited on
closed account (Ey80oG1T)
But in the exmaple I gave, you still have to define it right?
Not sure what you mean. See my edit above.
closed account (Ey80oG1T)
Oh, here's what I meant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// animal.h file

class Animal
{
public:
  void function();
};

// dog.h file

class Dog
{
public:
  // I want to overwrite the Animal::function(), so do I still have to define the function here?
}

// dog.cpp file

void Dog::function()
{
  // override
}
The function has to be defined somewhere, yes. The compiler can't magically know what it's supposed to do.

If you define it in the base class, then it will be inherited by the derived classes. You don't need to do anything in the derived class; it will inherit the base class method automatically.

If you want a derived class to behave differently when the method is called, then you can override the method by defining (and declaring) a specialised version of the function in the derived class.
I tend to pick the simplest solution that works, and I find I rarely need this type of inheritance, but when I do need it, there is nothing else quite like it, and trying to do some of the the things without it is painful (eg, try in C, which can do a basic class via function pointers and with some smoke and mirrors can even do 'inheritance' of sorts).

you can argue anything -- the above example can be done with just a string. ***
class animal
{
...
string response; //set this up in ctor or something
...
void hello() {cout << response << endl;}
}

*** this is the underlying problem. When learning this stuff, the examples are usually so trival there is a dozen simpler ways to do it. You need a complicated example to see when and where the tool pays off, but its difficult to learn from a gigantic complicated example.

If there is just one thing, the above is simpler than inheritance and I would choose the string all day long over in most scenarios for such a thing. But when you have something more interesting than a print statement, the extra effort of inheritance starts looking really nice.

A second answer is that 'yes, you can do most anything with the right pointers' but pointer heavy code is difficult to read, write, and debug. Remember that you can do anything that c++ does in assembly language too, but how much effort would it take? Inheritance is a tool to use when doing other approaches becomes ugly.
Last edited on
13
14
public:
  // I want to overwrite the Animal::function(), so do I still have to define the function here? 

You can define the specialised method Dog::function()j ust as you already have done in that code you've posted. But if you do so, you need to declare it in the Dog class definition, yes.

EDIT: and when doing so, you should use the override specifier, if you're using a modern version of C++.
Last edited on
Topic archived. No new replies allowed.