pure virtual functions and runtime id

C

cerenoc

I am fairly new to polymorphism with c++ and am having trouble
figuring out an error message. I narrowed it down to the following
simple example:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>

using namespace std;

class Base {
int temp1;
//string temp2;
public:
virtual void whoami() = 0;
};

class Derived1 : public Base {
public:
virtual void whoami() {
cout << "== Derived1 ==" << endl;
}
};

class Derived2 : public Base {
public:
virtual void whoami() {
cout << "== Derived2 ==" << endl;
}
};

int main(int argc, char *argv[]) {
int t = 0;
Base * var;

if (t == 1) {
Derived1 v;
var = &v;
var->whoami();
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

return 0;
}
----------------------------------------------------------------------

This code, as shown, compiles, runs and produces the expected result.
However, if I uncomment the 'string temp2;' line, the code runs to
produce the following error message:

==============
== Derived2 ==
pure virtual method called
terminate called without an active exception
Abort
==============

Could someone please explain this behavior and clue me in on how to
correct it? Thanks a lot.

J
 
J

Jim Langston

cerenoc said:
I am fairly new to polymorphism with c++ and am having trouble
figuring out an error message. I narrowed it down to the following
simple example:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>

using namespace std;

class Base {
int temp1;
//string temp2;
public:
virtual void whoami() = 0;
};

class Derived1 : public Base {
public:
virtual void whoami() {
cout << "== Derived1 ==" << endl;
}
};

class Derived2 : public Base {
public:
virtual void whoami() {
cout << "== Derived2 ==" << endl;
}
};

int main(int argc, char *argv[]) {
int t = 0;
Base * var;

if (t == 1) {
Derived1 v;
var = &v;
var->whoami();
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

return 0;
}
----------------------------------------------------------------------

This code, as shown, compiles, runs and produces the expected result.
However, if I uncomment the 'string temp2;' line, the code runs to
produce the following error message:

==============
== Derived2 ==
pure virtual method called
terminate called without an active exception
Abort
==============

Could someone please explain this behavior and clue me in on how to
correct it? Thanks a lot.


Look at this section of code:

} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

Derived2 v has a lifetime of the else block. After the block v goes out of
scope. Your 2nd call to var->whoami() is attempting to derefernce a pointer
to an instance that has gone out of scope. This is underfined behavior.
Undefined behavior means anything can happen, such as showing some output
when there is no variable defined ni the class or crashing when there is or
whatever, it is undefined.

How to correct it? Do not have your Base* point to a local instance of a
class then attempt to use it after the variable goes out of scope. This may
include using new or simply not attempting to access the variable after what
it points to goes out of scope, it depends on your program.

main() could be reduced farther to show this happening.

int main(int argc, char *argv[]) {
Base * var;
{
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

return 0;
}

It is only the block itself that causes the issue, doesn't matter if it's a
for block, if block, switch block or naked block as shown. Any variable
defined inside of a block has local scope to that block.
 
C

cerenoc

cerenoc said:
I am fairly new to polymorphism with c++ and am having trouble
figuring out an error message. I narrowed it down to the following
simple example:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
class Base {
int temp1;
//string temp2;
public:
virtual void whoami() = 0;
};
class Derived1 : public Base {
public:
virtual void whoami() {
cout << "== Derived1 ==" << endl;
}
};
class Derived2 : public Base {
public:
virtual void whoami() {
cout << "== Derived2 ==" << endl;
}
};
int main(int argc, char *argv[]) {
int t = 0;
Base * var;
if (t == 1) {
Derived1 v;
var = &v;
var->whoami();
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
return 0;
}
----------------------------------------------------------------------
This code, as shown, compiles, runs and produces the expected result.
However, if I uncomment the 'string temp2;' line, the code runs to
produce the following error message:
==============
== Derived2 ==
pure virtual method called
terminate called without an active exception
Abort
==============
Could someone please explain this behavior and clue me in on how to
correct it? Thanks a lot.

Look at this section of code:

} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

Derived2 v has a lifetime of the else block. After the block v goes out of
scope. Your 2nd call to var->whoami() is attempting to derefernce a pointer
to an instance that has gone out of scope. This is underfined behavior.
Undefined behavior means anything can happen, such as showing some output
when there is no variable defined ni the class or crashing when there is or
whatever, it is undefined.

How to correct it? Do not have your Base* point to a local instance of a
class then attempt to use it after the variable goes out of scope. This may
include using new or simply not attempting to access the variable after what
it points to goes out of scope, it depends on your program.

main() could be reduced farther to show this happening.

int main(int argc, char *argv[]) {
Base * var;
{
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

return 0;

}

It is only the block itself that causes the issue, doesn't matter if it's a
for block, if block, switch block or naked block as shown. Any variable
defined inside of a block has local scope to that block.

But isn't this the whole point of polymorphism? I want to be able to
have a function like someFunc and then be able to call the right
method with late binding?
------------------------
void someFunc(Base var);

int main(int argc, char *argv[]) {
Base * var;

if (...) {
Derived1 v;
var = &v;
} else if (...) {
Derived2 v;
var = &v;
}
someFunc(var);

return 0;
}
 
M

Martin York

cerenoc said:
I am fairly new to polymorphism with c++ and am having trouble
figuring out an error message. I narrowed it down to the following
simple example:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
class Base {
int temp1;
//string temp2;
public:
virtual void whoami() = 0;
};
class Derived1 : public Base {
public:
virtual void whoami() {
cout << "== Derived1 ==" << endl;
}
};
class Derived2 : public Base {
public:
virtual void whoami() {
cout << "== Derived2 ==" << endl;
}
};
int main(int argc, char *argv[]) {
int t = 0;
Base * var;
if (t == 1) {
Derived1 v;
var = &v;
var->whoami();
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
return 0;
}
----------------------------------------------------------------------
This code, as shown, compiles, runs and produces the expected result.
However, if I uncomment the 'string temp2;' line, the code runs to
produce the following error message:
==============
== Derived2 ==
pure virtual method called
terminate called without an active exception
Abort
==============
Could someone please explain this behavior and clue me in on how to
correct it? Thanks a lot.
Look at this section of code:
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
Derived2 v has a lifetime of the else block. After the block v goes out of
scope. Your 2nd call to var->whoami() is attempting to derefernce a pointer
to an instance that has gone out of scope. This is underfined behavior.
Undefined behavior means anything can happen, such as showing some output
when there is no variable defined ni the class or crashing when there is or
whatever, it is undefined.
How to correct it? Do not have your Base* point to a local instance of a
class then attempt to use it after the variable goes out of scope. This may
include using new or simply not attempting to access the variable after what
it points to goes out of scope, it depends on your program.
main() could be reduced farther to show this happening.
int main(int argc, char *argv[]) {
Base * var;
{
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
return 0;

It is only the block itself that causes the issue, doesn't matter if it's a
for block, if block, switch block or naked block as shown. Any variable
defined inside of a block has local scope to that block.

But isn't this the whole point of polymorphism? I want to be able to
have a function like someFunc and then be able to call the right
method with late binding?

Yes. But you can only call methods on an object that is still in scope
(alive). After the object goes out of scope it is unusable. You could
re-factor you code to make sure the objects do not go out of scope.

int main(int argc, char *argv[]) {
Base * var;
Derived1 v1;
Derived2 v2;

if (...) {
var = &v1;
} else if (...) {
var = &v2;
}
someFunc(var); // Both v1 and v2 are still in scope so now you can
use var

return 0;
}
 
J

Juha Nieminen

cerenoc said:
But isn't this the whole point of polymorphism?

The problem you presented has nothing to do with polymorphism, but
with object scope. Your derived objects are destroyed before the last
var->whoami() call. By all intents and purposes 'var' is not pointing to
an object of the derived class type anymore, because it has been
destroyed (because it went out of scope). That's why you are getting the
error.

As it happens, the object still physically exists because it's a
static instance, but most C++ compilers work in a way that when an
object of the derived type is destroyed, any virtual functions accessed
through a base class type pointer/reference will be purely of the base
class type, even if they point to this destroyed static object of the
derived type. This means that your pointer is not seeing a derived class
object anymore, only a base class object. That's why you are getting an
error message instead of a crash.

This behavior sometimes surprises even pros. For example, assume you
have a base class like this:

class Base
{
public:
~Base() { cleanup(); }
virtual void cleanup() { std::cout << "Base cleanup\n"; }
};

The idea with this would be that any derived class can implement the
'cleanup()' function, which will (in theory) be automatically called
when the object is destroyed. So you could do this:

class Derived: public Base
{
public:
virtual void cleanup() { std::cout << "Derived cleanup\n"; }
};

Now, assume that you have somewhere dynamically allocated instances of
different derived classes, for example like this:

std::vector<Base*> instances;

Then somewhere you destroy all the instances, like:

for(size_t i = 0; i < instances.size(); ++i)
delete instances;
instances.clear();

Now, it may become as a surprise that this will never print "Derived
cleanup", but will always print "Base cleanup" for all those objects,
even if the objects behind those pointers are of type Derived. The
'cleanup()' implementation of the derived objects is never called. This
attempt at a clever automatic cleaning up process failed.

The reason for this is that when the code reaches the 'cleanup()' call
in the base class destructor, the derived class part has already been
destroyed and by all intents and purposes doesn't exist anymore. From
the point of view of the base class destructor the object is purely of
type 'Base', nothing else. Because of this, the dynamic binding logic
calls the base class 'cleanup()' because that's what this object is at
this point. There simply is no way to call a derived class function from
a base class destructor.

Yes, it's a bummer, but it has a relatively simple solution:

class Derived: public Base
{
public:
~Derived() { cleanup(); }
virtual void cleanup() { std::cout << "Derived cleanup\n"; }
};

A bit less automatic, but that's just a side-effect of OOP.
 
J

James Kanze

On Feb 18, 1:40 pm, cerenoc <[email protected]> wrote:

[...]
Yes. But you can only call methods on an object that is still in scope
(alive).

Just a nit, but it really isn't a question of scope, but rather
of lifetime. In the case of a non-static local variable (an
"auto" variable), lifetime may correspond to scope, but this
isn't generally the case. The rule is that you can only call
member functions (virtual or not) on an object that exists.
After the object goes out of scope it is unusable. You could
re-factor you code to make sure the objects do not go out of
scope.
int main(int argc, char *argv[]) {
Base * var;
Derived1 v1;
Derived2 v2;
if (...) {
var = &v1;
} else if (...) {
var = &v2;
}
someFunc(var); // Both v1 and v2 are still in scope so now you can
use var
return 0;
}

I'd guess that 99% of the time, polymorphic objects will be
allocated dynamically. There are a few cases where you might
want to do something like:

Base const& obj(
someCondition
? static_cast< Base const& >( Derived1() )
: static_cast< Base const& >( Derived2() ) ;

but this doesn't work with all compilers; the object will be
destructed at the end of the full expression. (I think that
that's what the standard requires as well.) So you probably
have to use dynamic allocation here as well.

And of course, as soon as you start keeping the object alive,
and it can only be reached through pointers to Base or
references to Base, Base really should have a virtual
destructor.
 
J

James Kanze

The problem you presented has nothing to do with polymorphism, but
with object scope.

As I pointed out in my response to Martin York, it's not a
question of scope per se, but object lifetime. In the case of
auto variables (non-static variables with local scope), lifetime
does correspond to scope, but this is not a general principle.
Your derived objects are destroyed before the last
var->whoami() call. By all intents and purposes 'var' is not
pointing to an object of the derived class type anymore,
because it has been destroyed (because it went out of scope).
That's why you are getting the error.

For all intents and purposes, 'var' doesn't point to anything,
and just accessing var is undefined behavior. Anything can
happen.
As it happens, the object still physically exists because it's a
static instance,

First, the object(s) in question weren't static instances, but
local variables. And the objects don't "still exist". The
memory in which they were placed still exists (not guaranteed,
but true for most implementations), however, and it may contain
remenents of the object.
but most C++ compilers work in a way that when an object of
the derived type is destroyed, any virtual functions accessed
through a base class type pointer/reference will be purely of
the base class type, even if they point to this destroyed
static object of the derived type. This means that your
pointer is not seeing a derived class object anymore, only a
base class object. That's why you are getting an error message
instead of a crash.

What he's seeing is a pointer to nothingness. What's
doubtlessly actually happening is that without the std::string
member, the destructors are sort of trivial, doing nothing but
changing the vptr (the way virtual functions are usually
implemented); the compiler recognizes this, and doesn't bother
with them. And since nothing else has accessed the memory which
once contained the object, the code appears to work. With
std::string, the compiler must generate a destructor (to call
the non-trivial destructor of std::string), and this destructor
also updates the vptr to reflect the type in the destructor
(although in fact, the modified vptr will never be used).

Whatever happens, however, is really undefined. Even the
slightest changes in the source may result in the memory where
the object was being used, with desasterous consequences.
That's why the behavior is undefined.

[...]
The reason for this is that when the code reaches the 'cleanup()' call
in the base class destructor, the derived class part has already been
destroyed and by all intents and purposes doesn't exist anymore. From
the point of view of the base class destructor the object is purely of
type 'Base', nothing else. Because of this, the dynamic binding logic
calls the base class 'cleanup()' because that's what this object is at
this point. There simply is no way to call a derived class function from
a base class destructor.
Yes, it's a bummer, but it has a relatively simple solution:

It's a bummer that you can't call a function on something which
doesn't exist---whose class invariants no longer hold?
class Derived: public Base
{
public:
~Derived() { cleanup(); }
virtual void cleanup() { std::cout << "Derived cleanup\n"; }

};
A bit less automatic, but that's just a side-effect of OOP.

Or just have the derived classes put the cleanup code in the
destructor, where it belongs. (There are cases where a
"post-construtor" would be nice, i.e. code in the base class
that automatically runs immediately after the constructor of the
most derived class has finished, but I've never encountered
anything similar for a destructor, where I've wanted a
pre-destructor. Also, there are tricks which allow simulating a
post-constructor in most of the usual cases---although if you
use them, you should document very well what is going on,
because otherwise, you're going to have some maintenance
programmer really scratching his head.)
 
J

Jim Langston

cerenoc said:
cerenoc said:
I am fairly new to polymorphism with c++ and am having trouble
figuring out an error message. I narrowed it down to the following
simple example:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
class Base {
int temp1;
//string temp2;
public:
virtual void whoami() = 0;
};
class Derived1 : public Base {
public:
virtual void whoami() {
cout << "== Derived1 ==" << endl;
}
};
class Derived2 : public Base {
public:
virtual void whoami() {
cout << "== Derived2 ==" << endl;
}
};
int main(int argc, char *argv[]) {
int t = 0;
Base * var;
if (t == 1) {
Derived1 v;
var = &v;
var->whoami();
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
return 0;
}
----------------------------------------------------------------------
This code, as shown, compiles, runs and produces the expected
result. However, if I uncomment the 'string temp2;' line, the code
runs to produce the following error message:
==============
== Derived2 ==
pure virtual method called
terminate called without an active exception
Abort
==============
Could someone please explain this behavior and clue me in on how to
correct it? Thanks a lot.

Look at this section of code:

} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

Derived2 v has a lifetime of the else block. After the block v goes
out of scope. Your 2nd call to var->whoami() is attempting to
derefernce a pointer to an instance that has gone out of scope.
This is underfined behavior. Undefined behavior means anything can
happen, such as showing some output when there is no variable
defined ni the class or crashing when there is or whatever, it is
undefined.

How to correct it? Do not have your Base* point to a local instance
of a class then attempt to use it after the variable goes out of
scope. This may include using new or simply not attempting to
access the variable after what it points to goes out of scope, it
depends on your program.

main() could be reduced farther to show this happening.

int main(int argc, char *argv[]) {
Base * var;
{
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();

return 0;

}

It is only the block itself that causes the issue, doesn't matter if
it's a for block, if block, switch block or naked block as shown.
Any variable defined inside of a block has local scope to that block.

But isn't this the whole point of polymorphism? I want to be able to
have a function like someFunc and then be able to call the right
method with late binding?
------------------------
void someFunc(Base var);

int main(int argc, char *argv[]) {
Base * var;

if (...) {
Derived1 v;
var = &v;
} else if (...) {
Derived2 v;
var = &v;
}
someFunc(var);

return 0;
}

The "normal" way to handle this is like this:

int main(int argc, char *argv[]) {
Base * var;

if (...) {
var = new Derived1;
} else if (...) {
var = new Derived2;
}
someFunc(var);

delete var;
return 0;
}

An object allocated with new will remain until delete is called or the
program is terminated. Now there is no longer a scope issue (which brings
in the lifetime issue).

For polymorphism dyanmic allocation is normally used.
 
C

cerenoc

cerenoc said:
cerenoc wrote:
I am fairly new to polymorphism with c++ and am having trouble
figuring out an error message. I narrowed it down to the following
simple example:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
using namespace std;
class Base {
int temp1;
//string temp2;
public:
virtual void whoami() = 0;
};
class Derived1 : public Base {
public:
virtual void whoami() {
cout << "== Derived1 ==" << endl;
}
};
class Derived2 : public Base {
public:
virtual void whoami() {
cout << "== Derived2 ==" << endl;
}
};
int main(int argc, char *argv[]) {
int t = 0;
Base * var;
if (t == 1) {
Derived1 v;
var = &v;
var->whoami();
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
return 0;
}
----------------------------------------------------------------------
This code, as shown, compiles, runs and produces the expected
result. However, if I uncomment the 'string temp2;' line, the code
runs to produce the following error message:
==============
== Derived2 ==
pure virtual method called
terminate called without an active exception
Abort
==============
Could someone please explain this behavior and clue me in on how to
correct it? Thanks a lot.
Look at this section of code:
} else if (t == 0) {
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
Derived2 v has a lifetime of the else block. After the block v goes
out of scope. Your 2nd call to var->whoami() is attempting to
derefernce a pointer to an instance that has gone out of scope.
This is underfined behavior. Undefined behavior means anything can
happen, such as showing some output when there is no variable
defined ni the class or crashing when there is or whatever, it is
undefined.
How to correct it? Do not have your Base* point to a local instance
of a class then attempt to use it after the variable goes out of
scope. This may include using new or simply not attempting to
access the variable after what it points to goes out of scope, it
depends on your program.
main() could be reduced farther to show this happening.
int main(int argc, char *argv[]) {
Base * var;
{
Derived2 v;
var = &v;
var->whoami();
}
var->whoami();
return 0;
}
It is only the block itself that causes the issue, doesn't matter if
it's a for block, if block, switch block or naked block as shown.
Any variable defined inside of a block has local scope to that block.
But isn't this the whole point of polymorphism? I want to be able to
have a function like someFunc and then be able to call the right
method with late binding?
int main(int argc, char *argv[]) {
Base * var;
if (...) {
Derived1 v;
var = &v;
} else if (...) {
Derived2 v;
var = &v;
}
someFunc(var);
return 0;
}

The "normal" way to handle this is like this:

int main(int argc, char *argv[]) {
Base * var;

if (...) {
var = new Derived1;
} else if (...) {
var = new Derived2;
}
someFunc(var);

delete var;
return 0;

}

An object allocated with new will remain until delete is called or the
program is terminated. Now there is no longer a scope issue (which brings
in the lifetime issue).

For polymorphism dyanmic allocation is normally used.


Initializing the Derived objects as 'new' works and seems like the
simplest solution to me. Thanks for the lively discussion
 
J

Juha Nieminen

James said:
Or just have the derived classes put the cleanup code in the
destructor, where it belongs.

There may be cases where you want to clean up the object before it's
destroyed, for whatever reason. That's why it's sometimes handy to have
a separate cleanup (or whatever) function which can be called directly,
but will ultimately be called by the destructor.

It's not like this technique isn't used quite a lot. For instance, why
do you think there exists an explicit std::ifstream::close() function,
even though the stream is always closed automatically when the object is
destroyed?
 
J

James Kanze

There may be cases where you want to clean up the object before it's
destroyed, for whatever reason. That's why it's sometimes handy to have
a separate cleanup (or whatever) function which can be called directly,
but will ultimately be called by the destructor.
It's not like this technique isn't used quite a lot. For instance, why
do you think there exists an explicit std::ifstream::close() function,
even though the stream is always closed automatically when the object is
destroyed?

Because close() can change the state of the stream, and you have
to verify after having close(). The close() in the destructor
is just a safeguard, to prevent resource leaks in case of an
exception, and shouldn't really be used in normal programming,
unless you're abandoning all processing with the file (and in
the case of an output file, deleting it as part of the error
handling).

There are times when some sort of pre-destructor would be
useful, but I don't think they're very frequent.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,698
Latest member
LydiaHalle

Latest Threads

Top