0

Not sure where my inheritance went wrong here, but I seem to be only able to access the methods of the baseclass when storing a subclass instance in a baseclass pointer:

class Car
{
    public:
        Car():type("Car") {};
        Car(const char* t):type(t) {};
        void get_type() const { return this->type; };
    private:
        std::string type;
};

class Ford : public Car
{
    public:
        Ford():Car("Ford"), doors(4) {};
        Ford(int x):Car("Ford"), doors(x) {};
        void get_doors() const { return this->doors; };
    private:
        int doors;
};

int main()
{
    Car* c = nullptr;
    c = new Ford();
    c->get_doors(); // doesn't exist, only allows for get_type()
}

It's very likely it could be a misuse of the pointer. I'll admit that C++ is not my strong suit so I am trying to replicate a program written in Python that heavily uses inheritance but it's much less complicated than writing it in C++ since you don't have explicit use of Pointers and References (at the abstract level that is).

pstatix
  • 3,611
  • 4
  • 18
  • 40
  • You could make `get_doors()` a pure virtual function in `Car` and override it in `Ford`. It can't return `void`, though. – Fred Larson Aug 01 '18 at 20:47
  • @alterigel I thought I could use a `Car*` to point to any instance of a derived class from `Car`? That leaves no room for abstract class instantiation because there needs to be a single `Car` instance. – pstatix Aug 01 '18 at 20:47
  • 1
    What do you mean by "abstract class instantiation"? You can't instantiate an abstract class. – Fred Larson Aug 01 '18 at 20:49
  • 3
    Yes, you can use a `Car*` to point to any instance of a derived class from `Car`. But you are also limited to operations available to `Car`. (Because there is no guarantee that the thing your `Car*` points to supports `get_doors`. Imagine if the second line were `c = new Car();`.) – Raymond Chen Aug 01 '18 at 20:50
  • @FredLarson At runtime it will be unknown what type of `Car` subclass the user will create. So I would've thought that a `Car*` could point to any of the subclasses and allow access to not only `Car` members but that of the subclass as well? – pstatix Aug 01 '18 at 20:50
  • 1
    That's what virtual functions are for, as I mentioned in my first comment. C++ is statically typed and does not have "duck typing" as Python does. – Fred Larson Aug 01 '18 at 20:51
  • 1
    Try `static_cast(c)->get_doors();` – scopchanov Aug 01 '18 at 21:02

2 Answers2

5

Python methods are dynamic - pure object-oriented in the nature of smalltalk. You send an instance a message and if it can answer it, it does.

C++ is not like that. It uses static types. You have declared an object of type Car* so a) it can hold a pointer to any Car instance or instance of a subtype of Car and b) it can only be told to do what Cars can be told to do.

C++ also has virtual methods. If a method, declared in Car, is virtual, then it can be implemented in a subclass of Car (e.g., in Ford). Then that subclasses' implementation will be called when calling through a Car pointer.

davidbak
  • 5,775
  • 3
  • 34
  • 50
  • I ended up using the `virutal` keyword and read the docs, makes sense. Thanks! – pstatix Aug 01 '18 at 20:54
  • Addendum: This is done because C++ strives to make the program pay only for what the programmer asked for. Dynamic dispatch has a price, so in C++ you only pay that price if you ask for it. – user4581301 Aug 01 '18 at 20:56
1

This is exactly what should happen. A Car* pointer only lets you see Car members. Try using a Ford* pointer instead to see Ford methods. To see why, consider you had another class deriving from Car:

class Honda : public Car {
    // no get_doors() here
    void honk();
};

... 

Car* c = new Honda();
c->get_doors(); // What should happen here???

On the other hand, if you want every Car to have its own number of doors, you could do a couple things:

You can declare a constant member in the base class:

class Car {
public:
    Car(int num_doors) : m_doors(num_doors) {}

    int get_doors() const { return m_doors; }
private:
    const int m_doors;
}

class Ford : public Car {
    Ford() : Car(4) {} // Now every ford has 4 doors
}

class Honda : public Car {
    Honda() : Car(5) {} // and every honda has 5
}

Another thing you can do is declare a virtual function in your base class, and override that in every subclass:

class Car {
public:
    virtual int num_doors() const = 0;
}

class Ford : public Car {
    int num_doors() const override { return 4; } // Now every ford has 4 doors
}

class Honda : public Car {
    int num_doors() const override { return 5; } // and every honda has 5
}
alter_igel
  • 6,899
  • 3
  • 21
  • 40
  • I ended up using the `virtual` keyword, but not `override`. Instead, in `Car` I declared `virtual get_doors() const {};` and then in `Ford` I declared `virtual get_doors() const {return this->doors;};`. Do I need to use `override`? – pstatix Aug 01 '18 at 20:55
  • 1
    @pstatix: I recommend it. With `override`, if you make a mistake, you get a compile error. Without it, you get weird runtime behavior. – Fred Larson Aug 01 '18 at 20:57
  • Indeed, while `override` is optional, it can only do good. See https://stackoverflow.com/questions/18198314/what-is-the-override-keyword-in-c-used-for – alter_igel Aug 01 '18 at 21:00