Inheriting from a base class that inherits from another base class

Pages: 12
Indeed it does if I copy/paste to coliru. Must be at my end but the good side is I can see all the output.
Ch1156 wrote:

The only reason I can see to make a pointer to base class is to use it to make a generic animal. Unless it has pure virtual function in it then it cant be instantiated in main.

I could put them in a container of pointers to Animalia but what if Animalia class is an interface class?

This sounds rather like you're muddling up two completely separate things:
1. The type of object you're instantiating
2. The type of pointer in which you're storing the address of that object

It is perfectly OK to store the address of a concrete class in a pointer to an abstract base class. So, for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Abstract base class
class Animal
{
  virtual void myFunc() = 0;
}

// Concrete class
class Frog : public Animal
{
  virtual void myFunc() {};
}

Frog* myFrog = new Frog; // Fine, obvs
Animal* myAnimal = new Frog;  // Totally fine

Animal* myUnspecifiedAnimal = new Animal; // Illegal, because Animal is abstract

// Storing pointers to an abstract class in a vector
std::vector<Animal*> myMenagerie{myFrog, myAnimal};  // Fine 


My assumption is that I should not make virtual functions defined in Animalia virtual again in the two sub base classes but if I add something new to either Felidae or Equidae that is unique to al Felidae and Equidae then those should be virtual, correct?

If a function is defined as virtual in a base class, then if you override that function in any derived classes, the overridden version is always virtual, regardless of whether or not you use the virtual keyword.

However, I would recommend using the virtual keyword in those derived classes anyway, so that it's absolutely clear to whoever is reading the code that it's virtual, without needing to go looking back up the hierarchy to find the base class. So:

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
// Base class
class Animal
{
  // Both methods defined as virtual
  virtual void myFunc1() {};
  virtual void myFunc2() {};
}

// Subcategory that overrides base class methods
class Lizard : public Animal
{
  void myFunc1() {};  // This is still virtual, because the base class defined it as virtual
  virtual void myFunc2() {};  // Better, because you don't need to look at the base class to know it's virtual

  // New methods for Lizards only
  virtual void myFunc3() {};  // Virtual, obvs
  void myFunc4() {};          // Not virtual
}

// Subclass for a specific type of lizard
class Chameleon : public Lizard
{
  // Omitting the "virtual" as an example of how it would work.  In reality, it would be better
  // to put "virtual" in for those functions where polymorphism is wanted, to make it clearer.
  void myFunc1() {};  // Run-time polymorphism still applies to this
  void myFunc2() {};  // Run-time polymorphism still applies to this

  void myFunc3() {};  // Run-time polymorphism still applies to this
  void myFunc4() {};  // Run-time polymorphism does NOT apply to this
}


EDIT: Yes, smart pointers might be better, but I've omitted them for clarity
Last edited on

Ch1156 wrote:
The only reason I can see to make a pointer to base class is to use it to make a generic animal. Unless it has pure virtual function in it then it cant be instantiated in main.

I could put them in a container of pointers to Animalia but what if Animalia class is an interface class?

An object with pure virtual function, i.e. abstract class type cannot be instantiated.

Abstract base class IS the common interface of all derived classes.

We use pointer to base when we do not know the actual type of object during compile time.
Naive example: you read data for a shape from a file. You can't know what shape the file has. The read function might instantiate circle, triangle, or line object. It is easiest to get a Shape* to the new object and use that interface.

1
2
3
4
5
6
7
8
9
10
11
Aircraft* read( std::istream& in ) {
  // ...
  if ( /*something*/ ) {
    // ...
    return new F35Lightening2( params );
  } else {
    // ...
    return new C17Globemaster( params );
  }
  return nullptr;
}
We use pointer to base when we do not know the actual type of object during compile time.

Or because, in a particular context, we don't care what the actual type is - i.e. because we simply want to treat all the objects as having the base class interface, and can trust that run-time polymorphism will cause the correct method to be called, even though we're only using pointers to the base class.
Ok thank you, So I think I understand Virtual pretty well now.
unique_ptr<Fighter> fighter = make_unique<F35Lightening2>("F-35 Lightening II", "United States", 1200, 94000000.000, 29300.00, 2006, 625, "25mm Canon", "Air-to-air Missiles", 2000);
and not
unique_ptr<F35Lightening2> fighter = make_unique<F35Lightening2>("F-35 Lightening II", "United States", 1200, 94000000.000, 29300.00, 2006, 625, "25mm Canon", "Air-to-air Missiles", 2000);
( and similarly for Cargo etc smart pointer )
causes the crash occurs at run time.
Why the uniform typed versions aren't used in the first place is a mystery.
Last edited on
Topic archived. No new replies allowed.
Pages: 12