1

Please see my example:

#include <iostream>
using namespace std;

class base {
public:
    base() {}
    ~base() {}

    virtual void funcBase() {
        cout << "funcBase in class base" << endl;
    }
};

class derived : public base {
public:
    derived() {}
    ~derived() {}

    virtual void funcBase() {
        cout << "funcBase in class derived" << endl;
    }
    virtual void funcDerived() {} // I add virtual keyword to make sure funcDerived method is added in vtable of class
};

int main()
{
    base* obj = new derived();
    obj->funcBase(); // will print funcBase in class derived -> it's normal

    obj->funcDerived(); // error: 'class base' has no member named 'funcDerived'
    return 0;
}

So my question is, why can't I access the funcDerived method? I think the memory is of the derived class, so, the vpointer of class derived will point to vtable of it and this is ok!

Am I missing anything about the vtable and vpointer? What will be added to the vtable? Why is it restricted by pointer type?

Jason
  • 36,170
  • 5
  • 26
  • 60
Ngoc Hai
  • 11
  • 2
  • This is explained in any beginner level [C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). Refer to [how to ask](https://stackoverflow.com/help/how-to-ask) where the first step is to *"search and then research"* and you'll find plenty of SO related question for this. In particular, `obj` is a `base*` and you're trying to call `funcDerived` using `obj` while class `base` has no such method. Hence the error. – Jason Aug 28 '22 at 14:15

2 Answers2

3

obj is a pointer to base.
You can only call methods declared in base through it, even if the actual object pointed by it is a derived.
If these methods are polymorphic (i.e. virtual) the actual implementation that will be called will be that of derived (as you observed).

funcDerived is not declared in base and therefore cannot be accessed through a base pointer. The compiler issues an error because it cannot determine at compile time that the object is actually a derived one.

You can use dynamic_cast to cast the pointer to a derived one and then call funcDerived. Note that dynamic_cast can fail at run time and return nullptr if the object pointer is not actually a pointer to derived (or any class derived from it):

derived* obj_as_derived = dynamic_cast<derived*>(obj);
if (obj_as_derived)
{
    obj_as_derived->funcDerived(); 
}
else
{
    // Handle error ...
}

A side note: better to avoid using namespace std - see here: Why is "using namespace std;" considered bad practice?.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • thank you so much, The compiler issues an error because it cannot determine at compile time that the object is actually a derived one.Note that dynamic_cast can fail at run time and return nullptr if the object pointer is not actually a derived (or any class derived from it) <- I will remember it – Ngoc Hai Jul 16 '22 at 03:44
  • Is there a guarantee to get a `nullptr` if the base class is not of derived type? – Sebastian Jul 16 '22 at 08:15
  • If you use `dynamic_cast(obj)`, you are guaranteed to get `nullptr` if `obj` is not a `T*` (or pointer to a class derived from T). – wohlstad Jul 16 '22 at 09:32
  • BTW - If you do a `dynamic_cast` to a non-pointer type, you'll get an exception (std::bad_cast) instead of nullptr. But for `dynamic_cast` it will be a `nullptr`. See the dynamic_cast documentation link in my answer. – wohlstad Jul 16 '22 at 10:05
  • How does the C++ runtime know in the cases, when the classes have no vtable? – Sebastian Jul 16 '22 at 11:07
  • Ah, I see according to the doc it only works with polymorphic types to cast from base to derived. Otherwise you get a compilation error. – Sebastian Jul 16 '22 at 11:12
1
base* obj = (something blah blah);
[...]
obj->funcDerived(); // error: 'class base' has no member named 'funcDerived'

The above code won't compile because the compiler (in general) has no way of guaranteeing that a base * pointer (like obj) actually points to an object of class derived. For all the compiler knows, obj might point to a bare base object, or some other subclass of base that doesn't have a funcDerived() method defined; in either of those cases, trying to call a method that doesn't exist would be an error.

If you want to be able to call a virtual method via a base-class pointer, you'll need to declare that virtual method in the base class (as well as overriding it in the subclass; like what you did with funcBase()). That will let the compiler know that the method will always be present for any base object (or subclass thereof).

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 2
    "*If you want to be able to call a virtual method via a base-class pointer, you'll need to declare that virtual method in the base class*" - or, just use `dynamic_cast` to get a pointer to the derived class of the object. – Remy Lebeau Jul 16 '22 at 06:47
  • Well, okay, but then you’re calling the method via a derived-class pointer. – Jeremy Friesner Jul 16 '22 at 13:32