pure virtual functions and runtime id

Discussion in 'C++' started by cerenoc, Feb 18, 2008.

  1. cerenoc

    cerenoc Guest

    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
     
    cerenoc, Feb 18, 2008
    #1
    1. Advertising

  2. cerenoc

    Jim Langston Guest

    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.

    --
    Jim Langston
     
    Jim Langston, Feb 18, 2008
    #2
    1. Advertising

  3. cerenoc

    cerenoc Guest

    On Feb 18, 4:20 pm, "Jim Langston" <> wrote:
    > 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.
    >
    > --
    > Jim Langston
    >


    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;
    }
    -------------------------
    And this does work, except for the case when I have a string variable
    declared in the abstract base class.
     
    cerenoc, Feb 18, 2008
    #3
  4. cerenoc

    Martin York Guest

    On Feb 18, 1:40 pm, cerenoc <> wrote:
    > On Feb 18, 4:20 pm, "Jim Langston" <> wrote:
    >
    >
    >
    > > 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.

    >
    > > --
    > > Jim Langston
    > >

    >
    > 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;
    }
     
    Martin York, Feb 18, 2008
    #4
  5. cerenoc wrote:
    > 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.
     
    Juha Nieminen, Feb 19, 2008
    #5
  6. cerenoc

    James Kanze Guest

    On Feb 19, 12:14 am, Martin York <> wrote:
    > On Feb 18, 1:40 pm, cerenoc <> wrote:


    [...]
    > > 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).


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Feb 19, 2008
    #6
  7. cerenoc

    James Kanze Guest

    On Feb 19, 10:08 am, Juha Nieminen <> wrote:
    > cerenoc wrote:
    > > But isn't this the whole point of polymorphism?


    > 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.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Feb 19, 2008
    #7
  8. cerenoc

    Jim Langston Guest

    cerenoc wrote:
    > On Feb 18, 4:20 pm, "Jim Langston" <> wrote:
    >> 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.
    >>
    >> --
    >> Jim Langston
    >>

    >
    > 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;
    > }
    > -------------------------
    > And this does work, except for the case when I have a string variable
    > declared in the abstract base class.


    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.
    --
    Jim Langston
     
    Jim Langston, Feb 19, 2008
    #8
  9. cerenoc

    cerenoc Guest

    On Feb 19, 8:29 am, "Jim Langston" <> wrote:
    > cerenoc wrote:
    > > On Feb 18, 4:20 pm, "Jim Langston" <> wrote:
    > >> 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.

    >
    > >> --
    > >> Jim Langston
    > >>

    >
    > > 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;
    > > }
    > > -------------------------
    > > And this does work, except for the case when I have a string variable
    > > declared in the abstract base class.

    >
    > 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.
    > --
    > Jim Langston
    >



    Initializing the Derived objects as 'new' works and seems like the
    simplest solution to me. Thanks for the lively discussion
     
    cerenoc, Feb 19, 2008
    #9
  10. James Kanze wrote:
    >> 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 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?
     
    Juha Nieminen, Feb 20, 2008
    #10
  11. cerenoc

    James Kanze Guest

    On Feb 20, 5:16 pm, Juha Nieminen <> wrote:
    > James Kanze wrote:
    > >> 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 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Feb 20, 2008
    #11
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dario
    Replies:
    9
    Views:
    7,420
    Craig Powers
    Sep 20, 2003
  2. IK
    Replies:
    2
    Views:
    635
    hemraj
    Jul 23, 2004
  3. Todd Aspeotis
    Replies:
    3
    Views:
    498
    Kanenas
    May 30, 2005
  4. John Goche
    Replies:
    10
    Views:
    796
    Marcus Kwok
    Dec 8, 2006
  5. a
    Replies:
    7
    Views:
    383
    dasjotre
    Jun 28, 2007
Loading...

Share This Page