1

I have a base class and 2 inherited classes that inherit from the base class. The base class shouldn't be instantiated so I want to make an abstract base class, but the 2 inherited classes have no shared functions that have the same signature (the signature is similar but not identical), so it doesn't make a whole lot of sense to make a pure virtual function in the base class unless I just make a dummy virtual function and define it in the inherited classes. Is there another approach to this?

Here's an example:

class base {
  public: 
    base() : i{0}, j{0} {}
    
    void compute_i() {
      // computes i
    }

  private:
    // declare some variables
    double i;
    double j;
}

class inherited1 {
  public:
    inherited1() = default;

    void compute_j(double val, double k) {
      // compute j here
    }
}

class inherited2 {
  public:
    inherited2() = default;

    void compute_j(double val) {
      // compute j here
    }
}

roulette01
  • 1,984
  • 2
  • 13
  • 26
  • You can do that, but the function in the child-classes will not override the base-class function. Instead they will *shadow* it, and there's no polymorphism involved (which I assume is the goal?). – Some programmer dude Mar 29 '23 at 16:13
  • @Someprogrammerdude Yeah, I'm trying to achieve polymorphism – roulette01 Mar 29 '23 at 16:18
  • @PaulSanders Let me rephrase. Ideally I would like to achieve polymorphism, but the parameter `k` is needed for the first inherited class. So the only way to align the signature is to introduce that parameter in `compute_j` for `inherited2` and not use it, but I feel like that's poor design – roulette01 Mar 29 '23 at 16:21
  • 4
    This looks like a bad fit for inheritance. – molbdnilo Mar 29 '23 at 16:22
  • How about `void compute_j(double val, double k = 0)` in `inherited1`? – Paul Sanders Mar 29 '23 at 16:23
  • @PaulSanders But `k` still remains unused which I feel hampers readability – roulette01 Mar 29 '23 at 16:28
  • 1
    `2 inherited classes have no shared functions that have the same signature`. This is a violation of the Liskov substitution principle, which means this is not polymorphism, and therefore having a base class is probably not the right way to code the classes. – Mooing Duck Mar 29 '23 at 16:30
  • @MooingDuck But they both share `compute_i`, it's just that the definition is the same for both and placed in the base class? Does that not count? – roulette01 Mar 29 '23 at 16:32
  • 1
    You could make the constructor of the base class `protected`. See [What are practical uses of a protected constructor?](https://stackoverflow.com/q/1057221/1563833) – Wyck Mar 29 '23 at 17:00
  • Perhaps your design is flawed? Do you really need inheritance here? Remember that inheritance is an "is-a" relationship, are `inherited1` and `inherited2` really `base` objects? Just because you want to share some functions doesn't mean inheritance might be the right choice, perhaps plain functions using overloading would suffice? Perhaps coupled with composition? – Some programmer dude Mar 29 '23 at 23:43

2 Answers2

1

There is a function which exits by default in all inherited classes, and you can declare it as pure virtual. It is the destructor. The destructor is the only function needing a body when it's pure virtual.

class base {
  ...
  virtual ~base() = 0;    // destructor is pure
  ...
};
base::~base() = default;  // destructor body

Notice that, if a class is a base class, the destructor must be either virtual or protected. So here, you must define one destructor.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
dalfaB
  • 436
  • 1
  • 7
0

To make it so that you can't instantiate base, just declare the constructor of base protected:

class base {
  protected: 
    base() : i{0}, j{0} {}

  public:  
    void compute_i() {
      // computes i
    }

  private:
    // declare some variables
    double i;
    double j;
};

class inherited1 {
  public:
    inherited1() = default;

    void compute_j(double val, double k) {
      // compute j here
    }
};

class inherited2 {
  public:
    inherited2() = default;

    void compute_j(double val) {
      // compute j here
    }
};

int main ()
{
//  base b;              // does not compile
    inherited1 i1;
    inherited2 i2;
}

Live demo

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • With this solution the base is not really abstract and it can be instanciated, e.g. the inherited classes can return a base object. – gerum Mar 29 '23 at 17:10
  • @gerum Well they _can_, but why would they? – Paul Sanders Mar 29 '23 at 22:39
  • That argument would work for all syntactic sugar. Why do we need abstract classes, if you can simply not instantiate it. Why do we need const, we simply do not modify a value. The point is, that we want to prevent some things. – gerum Mar 30 '23 at 06:58