Cortex0101
Cortex0101

Reputation: 927

Should superclasses have constructors?

Sorry if this seems like a dumb question, but I really feel like I'm falling down a deep rabbit hole here.

I have a really hard time understanding superclass constructors. I get that it can be useful to have a handful of functions and attributes that all subclasses can inherit from, but let's say I have a class animal that all animals inherit from.

It has a couple of attributes and methods that all animals have. For instance:

class Animal {
    bool alive;
    int weight;

    void reproduce();
};

These are clearly some of the few things that relate to all types of animals.

I see how it's useful to put these attributes/methods in a superclass, furthermore, we could then make subclasses for animals that relate to different groups of animals eg:

class Mammal : public animal {
    bool hasGivenBirth;

    void getPregnant();
};

And then go to they're gender and so on.

But, would we ever actually construct an Animal or Mammal? I don't see how that makes sense since all animals will fall into a very specific subcategory.

Thus my question is how constructors in superclasses make sense, and how would I use them?

Upvotes: 0

Views: 45

Answers (1)

Caius Jard
Caius Jard

Reputation: 74605

A constructor is a method that you can be certain is called upon construction of a class instance, because the behaviour is enforced. Hence you can put code in the construction to ensure that the class is ready for use

In the case of a superclass, though you might not construct one directly, your child classes can receive some parameters (or you can hardcode them) and pass them to the parent constructor for action rather than doing the actioning themselves.

In your animal case, suppose an animal has a number of legs. All animals do, so it's a property of Animal, rather than Dog. Your dog constructor can set the number of legs if it has access to it, or it can call the base constructor passing the number of legs if it doesn't.

By declaring Aninal to have a constructor that demand the number of legs, you won't ever forget to create a child class that omits the number of legs, because you won't be able to call the base constructor without supplying it

class Animal{
  Animal(int numberOfLegs){}
}

class Dog:Animal{
  Dog(string breed): base(4) {} //call base constructor
}

class Cat:Animal{} //doesn't work; error "no argument is given that corresponds to formal parameter 'numberOfLegs'"

If you don't provide a constructor in a particular class, the compiler will silently insert a blank parameterless one for you that does nothing other than call the base parameterless one. If you do provide a constructor, it won't create a blank one. This is what gives rise to the error "no argument given corresponds..." - the compiler has auto inserted a constructor into Cat that calls Animal() but there isn't a parameterless one and the compiler won't guess at what int to provide for the numberOfLegs.

Hence because Animal has a constructor that demands a number of legs, we are forced to provide a value for it somehow (we could have accepted a parameter in Dog's constructor to allow an external code to declare the number of legs) - in essence we cannot construct any subclass of Animal unless we create a constructor in the subclass that calls base(some_int_here).

If an animal would fail to work without its leg count being set, this mechanism ensures that leg count is set (I didn't show the setter code that assigns leg count internally within Animal, for sake of brevity)

Thus, yes superclasses should have constructors - all classes should (and do) have constructors (it's "the law"). Sometimes constructors are blank, and sometimes they do stuff

You also said you don't see how we'd ever construct just a plain up Animal rather than a specific Dog, Cat etc

You're right, we might not.. but the beauty of OO is that we can use Animal to generically refer to all its children. We can make an array of Animal, and put 10 different specific animals in there, and use their common properties and methods without knowing or caring what they are. In the real world we treat things genetically all the time by using their common properties. I've never driven a Mercedes, but it probably works mostly the same as my Ford, pedals in the same place and do the same thing when pushed. We don't always care about the specific details of every object in our lives, that the ford has a cable clutch and the merc a hydraulic one; it just works the same when we push the pedal. Similarly our array of Animal, we can just traverse it and add up the number of legs without needing to know each type of animal

Animal[] x = new Animal[10]();
x[0] = new Dog();
...

This doesn't construct an Animal, it constructs a dog, but animal's constructor is called as part of the process. At the end of it we can add up the legs by accessing an animal's NumberOfLegs without saying "if the array element is a dog then add 4 else if ..."

This is why base classes have attributes, and also constructors (because they might need some setup too)

Upvotes: 1

Related Questions