user2227017
user2227017

Reputation: 9

Virtual function of derived class causes "not a member" error when called from base

#include<iostream>

class A {
private :
    int _a;

public :
    A(int i=0) : _a(i){}
    virtual void setA(int i) { _a = i;}
    virtual int getA(){ return _a;}
};

class B : public A
{
private :
    int _b;

public :

    B(int i) : _b(i){}
    virtual void setB(int i) { _b = i;}
    virtual int getB(){ return _b;}
};

int main(){
A* a1 = new B(10);
std::cout << a1->getA() << "\t " << a1->getB() << std::endl;
return 0;
}

While compiling I get the following error:

test.cpp(28): error C2039: 'getB' : is not a member of 'A'

Shouldn't a1 get getB since it's actually a pointer to B?

Upvotes: 0

Views: 947

Answers (4)

Mats Petersson
Mats Petersson

Reputation: 129314

The compiler tells you the exact reason:

'getB' : is not a member of 'A'

The compiler does a compile time checking based on the type of the pointer, since it cannot find the method in A it reports so. Note that at compile time the compiler may or may not know what object the pointer a1 points to, it might be A or B or any class publically derived from A. The compiler only checks type of the pointer and checks if the member being accessed belongs to that class.
For you, a1 points to a object of B and hence can be treated like B but for the compiler a1 is a pointer of the type A and at compile time it only treats it as A.


Whilst the answer to "why this doesn't work" is well covered above, I think I should but in on the "how do you solve this".

The simple answer is to implment a getB() for A. This is generally the right thing to do - and if it's somehow "forbidden" to call getB on objects that don't have this property, then do something about it - print an error, exit the program, or whatever is "right" when someone does something wrong in the code (preferrably print a message somewhere, then stop, so that it's clear what has gone wrong and it can be debugged).

The alternative, using for example dynamic cast, will also work. But you still have to either know whether the object is a B class or not - or check if the pointer returned NULL.

The whole point with virtual functions is that you override the ones in the baseclass with new variants in the derived class. It is not there to support adding NEW functions in the derived classes. The way to support "new functions" is to for example add a parameter:

class A
{
 public: 
    ...
    virtual int get(char v)
    {
       if (v == 'A') return _a;
       else
       {
          std::cerr << "Error: Requesting invalid variable: '" << v << "'" << std::endl;
          return -1;
      }
    }
    ...
}


class B: public A
{
   ...
   virtual get(char v)
   {
      if (v == 'B')
         return b;
      return A::get(v);
   }

   ...
}

Now this works with a slight modification:

std::cout << a1->get('A') << "\t " << a1->get('B') << std::endl;

Upvotes: 3

witrus
witrus

Reputation: 297

Though a1 is a pointer to a class B object,compiler does not know a1 points to a B object until run time.So in compile time,you will encounter an error because there is no getB method in class A.

Upvotes: 0

David G
David G

Reputation: 96790

The compiler doesn't know about a's dynamic type information. You can cast it to its dynamic type using dynamic_cast:

dynamic_cast<B*>(a1)->getB();

Live Demo

Upvotes: 1

Johnny Mnemonic
Johnny Mnemonic

Reputation: 3912

You must have a member function in the class A:

virtual int getB()
{
 // do something or not
}

And after that when you call a1->getB() it will call the member function of class B.

An alternative would be to use dynamic_cast on a1:

A* a1 = new B(10);
B* b1=dynamic_cast<B*>(a1);
std::cout << b1->getA() << "\t " << b1->getB() << std::endl;

Upvotes: 1

Related Questions