Whoami
Whoami

Reputation: 14408

Unable to invoke derived class function in C++

I'm sure I missed some part of the code.

I have the following code:

#include <iostream>

using namespace std;

class Record
{
private:
    int age;
    string name;
public:
    virtual int getType()=0;
};


class Student:  public Record
{
  private:
     int level_;
  public:
     Student()
     {
        level_=1;
     };
     ~Student() {};
     int getType()
     {
        return 1;
     }

     int level()
     {
        return level_;
     }
};


int main (int argc, char ** argv)
{
  Record *r = new Student();
  cout <<"tuype " << r->getType();
  cout <<"Class " << r->level();
}

Question is : Why am I not able to invoke r->level()?. What changes are required to get it invoked?

Upvotes: 3

Views: 197

Answers (7)

lulyon
lulyon

Reputation: 7225

student instance is upcasted to Record by:

Record *r = new Student();

While hold a Student instance, r behalf as Record. The r->getType() function call is binded to student::getType with C++'s Polymorphism machanism. To call level() function, you can:

1. Add a virtual function level() to Record class.
2. downcasting r to Student class, as follows:
Student *r_new = dynamic_cast<Student>(r); 
r_new->level();

Upvotes: 1

Tony Delroy
Tony Delroy

Reputation: 106096

Changes to Record to make level() virtual

You wrote:

Record *r = new Student();

After that line, the compiler considers r to be a pointer to either a Record or some Record-derived class (which it is), but it's only aware of the interface specified for Record. There is no virtual level() function in Record, so you can not access Student's level function through the Record interface. Just add such a function to Record and you'll be ok:

virtual int level() { return 0; }  // Student may override implementation

or

virtual int level() = 0;   // Student MUST override implementation

An alternative: checking whether a Record* addresses a Student

I say above...

There is no virtual level() function in Record, so you can not access Student's level function through the Record interface.

...and show how you can add to the Record interface, but an alternative is to get access to the Student interface again, as in:

if (Student* p = dynamic_cast<Student*>(r))
    std::cout << "Level " << p->level() << '\n';

The first line checks whether the Record* r happens to be pointing to a Student (of course in your code it always is, but imagine you were inside a function that accepted a Record*, or were looping over a container of such pointers where some were really Students and others not). If so the returned pointer can be used to access it as Student object, with any extra functionality/members available (and potentially restrictions, if some Record functionality's hidden in some way).

This approach is generally frowned upon, as it begs the question "why were we treating the Student as a Record if we needed to know "level" and other Record-derived types don't even have a concept of level?". Still, stuff like that happens sometimes. Adding a virtual level function to Record isn't ideal either if Student is (one of) the only derived-class(es) where it would have a meaningful value: that's what's called a fat interface - you'll find a few discussions of them in The C++ Programming Language if you have a copy.

(sasha.sochka's answer was the first to mention the dynamic_cast option - please upvote)

Base class should have virtual destructor

As per chris's comment, you should add to Record:

virtual ~Record() { }

This ensures a derived class's destructor implementation is called when a derived object is deleted using a base-class pointer (for example, if you add a delete r; at the bottom of main()). (The compiler will still ensure the base class destructor is called afterwards).

It's Undefined Behaviour if you don't do this, and at best you'll find any additional data members added in derived classes don't have their data members called... for an int that's harmless, but for say std::string it could leak memory, or even hold a lock such that the program later hangs. Of course, it's not a good idea to rely on the best-case Undefined Behaviour ;-) but I think it's useful to understand what's definitely not happening unless you make the base destructor virtual.

Recommended small improvements in Student

If you make level virtual in Record, then to make it clearer to readers of the Student class that level() is an implementation of a virtual function from a base class, you can use the override keyword if you have a suitably C++11-capable compiler:

int level() override
{
    return level_;
}

This will give you a compiler error if it can't find a matching (non-const) virtual int level() in a base class, so it can avoid some occasional troubleshooting. You may also repeat the virtual keyword if you feel it has documentation value (particularly nice for C++03 where override's not an option), but it doesn't make any functional difference - the function stays virtual as long as it's (implicitly or explicitly) an override of a virtual function from a base class.

Upvotes: 8

sasha.sochka
sasha.sochka

Reputation: 14715

You cannot call r->level() because you are trying to call a function which doesn't exist in class Record. In your specific case data pointed by r is not only Record but a Student in the same time, so you can choose either:

Student *r = new Student();
cout <<"tuype " << r->getType();
cout <<"Class " << r->level()

or

Record *r = new Student();
cout <<"tuype " << r->getType();
cout <<"Class " << dynamic_cast<Student*>(r)->level()

If you want all Records to have a level you can add pure virtual function without implementation. But if you do that you will be unable to create instantate objects of class Record (but you can instatntiate it's children classes):

class Record { ...
   virtual int level() = 0;
}

Another problem: you should mark your destructor in Record class as virtual, because when you will delete r constructor of Student will not be called

Upvotes: 3

bobah
bobah

Reputation: 18864

You are missing virtual method level() declaration in Record. On a side note, to prevent resources leak you would want to define virtual destructor in Record.

class Record
{
...
public:
  virtual ~Record() {}
  virtual int level() = 0;
  virtual int getType() = 0;
};

Upvotes: 1

Shumail
Shumail

Reputation: 3143

Make virtual function in base class like

virtual int level() = 0;

Once you create virtual function level() in base class Record, it becomes necessary for student to have it's function level() in student class. Currently you don't have virtual level() function in class Record, because of this you cannot access Student class's level() function using base class Record as you are doing currently.

Upvotes: 2

Korchkidu
Korchkidu

Reputation: 4946

When compiling, you should get an error message similar to:

error C2039: 'level' : is not a member of 'Record'

You need to add this in your Record class:

virtual int level()=0;

Upvotes: 0

Salgar
Salgar

Reputation: 7775

Because the Record class knows nothing about a function called level()

You need to make a virtual level() function in the base class.

Upvotes: 2

Related Questions