0

As the title states, I have a base class (interface) and several derived classes. On the derived classes, I have some extra functions that don't make sense to implement on the interface class - neither on all derived classes.

In my program, I have a vector of pointers to base classes - but the actual objects are all of derived classes.

Now, if I try to call a function of a derived class, I get a compiler error. Even if I know for sure the derived type that a particular object will have, I cannot call the function.

How do I get around this? Would this point to a design problem? Should I convert the pointer/create a new pointer of that specific type?

The last option seems to work, but makes the code very ugly with tons of if-else blocks.

Here is some example code to reproduce the error:

#include <iostream>

class Base {
public:
    Base() {};
    ~Base() = default;
};
    
class Derived: public Base {
public:
    Derived() {};
    ~Derived() = default;
    void foo() {
        std::cout << "foo()" << std::endl;
    }
};
    
int main()
{
    Base* ptr = new Derived();
    *ptr->foo();
    delete ptr;
}

EDIT: I do know about virtual functions, but this is not what I'm looking for. My point is that it would make no sense to implement foo() on the base class nor on all of the derived classes. foo() is something very specific to one specific derived class. Example: my base class is Vehicle, my derived classes are Car, Helicopter, Speedboat. My function is checkTirepressure() - as you see this function doesn't make any sense on Helicopter or Speedboat.

  • You mean ptr->foo() – pm100 Dec 17 '22 at 14:05
  • It wouldn't work even as `ptr->foo()`, though. The `Base` class does not have a member function called `foo`. – kotatsuyaki Dec 17 '22 at 14:05
  • Does this answer your question? [How to call derived class method from base class pointer?](https://stackoverflow.com/questions/18885224/how-to-call-derived-class-method-from-base-class-pointer) – kotatsuyaki Dec 17 '22 at 14:06
  • 1
    Option 1: provide virtual functions in the base class, which the derived classes can override/specialise as needed. [Bear in mind the destructor needs to be `virtual` as well, otherwise `delete ptr` has undefined behaviour]. Option 2: if you *know* (and have done checks if needed) that a `Base *` actually points at an instance of `Derived`, then explicitly convert the pointer to a `Derived *` and call the member function [if the object is *not* of the derived class, then this also causes undefined behaviour]. Note: `*ptr->foo()` is invalid syntax in your example - remove the `*`. – Peter Dec 17 '22 at 14:08
  • 1
    Do you know what virtual methods are, how they work, and how to use them? – Sam Varshavchik Dec 17 '22 at 14:10
  • How can the compiler work out when the type of your pointer is `Base*` that you will actually have a `Derived` object when your program is run? That's what you are asking for. Virtual functions and polymorphism generally is what you need to read up on. – john Dec 17 '22 at 14:12
  • Either have the base class with those virtual functions, or use one different container per derived type. Other options are hacks IMO – Jeffrey Dec 17 '22 at 14:12
  • 1
    If you know for sure, use `static_cast` to convert `Base*` to `Derived*` and then call the method. You can use `dynamic_cast` if you don't know for sure, it will check for you and return `nullptr` if failed. However, a properly designed interface wouldn't need that; the whole point is to pull all common behavior in the interface. If you have a bunch of `if`s, make them a function in the interface. – yeputons Dec 17 '22 at 14:13
  • Thanks for the replies. The static_cast/dynamic_cast does do the trick. But that solution seems to be at least frowned upon here. But also it seems silly to define a bunch of functions on the base class that make no sense. Like if my base class is Shape and I derive both a Square as well as a Point class from that - having "getCircumference()" on the Shape class doesn't make sense to me as there are multiple examples (e.g. point) which have no circumference. It would be much more precise to implement it only where it does. – phantum12265 Dec 17 '22 at 14:39
  • @phantum12265 That's the whole point. If it doesn't make sense for all shapes to have a `getCircumference()` function, then you shouldn't be able to call `getCircumference()` on a `Shape`! If you *know* that a shape is a circle, then you have to cast it to a `Circle&`/`Circle*` in order to call the functions only applicable to a circle – Kevin Dec 17 '22 at 17:11
  • @Kevin Yes, casting works, thanks! I'm happy to use that for now, but yeputons mentioned "a properly designed interface wouldn't need that" - so I wonder how to get around this without casting. But I can't even think of a solution, much less write one. – phantum12265 Dec 18 '22 at 14:36

2 Answers2

0

To run the function of derived class from the base class pointer, make sure that the function you want to run have same name and return type function in base class and in addition make sure that the function in base class has virtual keyword before to its return type. Let me explain with an example....

#include<iostream>
using namespace std;

class base{
public:
virtual void func(){
    cout<<"hello";
 }
};

 class derived: public base
{
 public:
 void func(){
     cout<<"Hi";
 }
 };

  int main(){
  base* b1=new derived;
  b1->func();
  return 0;
  }

here the base class pointer is pointing to function of derived class...

Understand that thing the function you want to run should have same name and return type in both of the classes but in addition there is the virtual key word before this function in the base class.

Asjid Ali
  • 57
  • 4
  • Thanks for your answer, but it's not really what I'm looking for. My point is that foo() is very specific to the derived class - it doesn't make sense to put foo on the base class - and subsequently to ALL other derived classes. Imagine the base being a Object2D and derived from that is for example a Point2D and a Square. The Square class has a function getCircumference() - but it doesn't make sense to put that function on Object2D as not every derived class (in this case for example the Point2D) has a circumference. – phantum12265 Dec 18 '22 at 14:14
  • @phantum12265 I think actually you are little bit confused but you would get further clear when you would study pure virtual functions – Asjid Ali Dec 19 '22 at 13:55
0

Yes there is a way around it, by something what is called "down casting" but you do not want to go there, as you already mentioned in your question - would it be a design problem? Yes, it would.

Instead you need to understand the basic differences between compile time polymorphism and runtime polymorphism. You in your given sample are trying to achieve runtime polymorphism.

Try to think of it this way:

When you are creating an interface (base class), you are creating something that demonstrates what functionalities are available. In a train it would be the main controls that allow you to operate the train.

The implementation (derived class) of that interface is hidden from you (you don't see how the red button starts up the engine).

In programming it is the same. In your interface (base class) you need to define what will be available to you, but you don't have to implement it. To do this you need the virtual keyword to define a (pure) virtual function, which you define later. I won't go into detail how virtual functions work (you can read about that yourself). I.e.:

#include <iostream>
class Base {
public:
    Base() {};
    ~Base() = default;
    //foo() is virtual and can have it's own implementation
    //you can still override this function in the derived class
    virtual void foo() {std::cout << "foo";}; // virtual function
    //pure virtual functions are without an implementation and must be
    //implemented in their derived classes
    virtual void bar() = 0; //pure virtual 
};

Now in your sample what you are doing (without virtual functions) is the following:

int main()
{
  //You are creating a pointer to Base class, your compiler knows
  //this during compile time. During runtime you are allocating
  //a Derived class and pointing to it with the Base ptr
  Base* ptr = new Derived(); 
  //During compile time it doesn't know that you are supposed to
  //be able to call this function, because it still sees it as a 
  //Base class. If this would pass the compilation it would have 
  //a problem during runtime as well! It wouldn't be able to find
  //the function, because you didn't tell the program that Base 
  //class is supposed to in the case of polymorphism
  //expect such a function (it did not create a hidden pointer in
  //it's vtable that could potentially store the function pointer)  
  *ptr->foo();
  delete ptr;
}

I recommend reading about:

  1. Virtual method table
  2. Polymorphism

Hope it helps!

Milan Š.
  • 1,353
  • 1
  • 2
  • 11
  • Thanks a lot. Unfortunately it's not quite what I'm looking for. To use your example: There's a Train class and derived form that are SteamTrain and ElectricTrain classes. Now SteamTrain has a function checkCoal(). But this function ONLY belongs on the SteamTrain, it doesn't make any sense to put it on ElectricTrain (has no coal). Vice Versa ElectricTrain probably has many functions that don't make sense on SteamTrain. Should I put all these functions on the Base class even though at best it will only make sense to 50% of the derived classes? That seems very wasteful. – phantum12265 Dec 18 '22 at 14:20
  • Let's say that the electric train has an equivalent to checkCoal() and it's (for example) checkElectricCurrent(). Then you should abstract and create a function in base class called "checkEnergySupply()". The whole idea behind polymorphism is to group together similar objects but with different implementation - a good example are containers. You need to Add and Delete function. But container A needs to allocate space on heap then add, while container B uses already existing allocated memory. – Milan Š. Dec 18 '22 at 15:25
  • Other than that you are going down the road of very bad and poor design. Down casting (which some of the people on your original post already mentioned) is something you should avoid at all costs. (that's the reinterpret/dynamic/static cast for example) Edit: Also don't forget you can always abstract more. Train can be the interface class but you can have something like AbstractTrain -> that is inherited by OldTrain and NewTrain ... OldTrain -> CoalTrain, NewTrain ->ElectricTrain... etc. – Milan Š. Dec 18 '22 at 15:26