Reputation: 14408
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
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
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 inRecord
, so you can not accessStudent
's level function through theRecord
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 Student
s 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 delete
d 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
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 Record
s 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
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
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
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
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