C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Pure Virtual Function call error

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Dave
Guest





PostPosted: Thu Jul 29, 2004 9:23 pm    Post subject: Pure Virtual Function call error Reply with quote



Hi,
I have tried to use one of Herb Sutters examples from his book
Exceptional C++ It is Item 23 and introducedsloads of cool concepts. I
have implemented the template pattern and then used the _pimpl idiom
and all was going well. He
then suggest splitting functionality further:


Generic.h
---------


class GenericClient;

class GenericAlgorithm
{
public:

GenericAlgorithm(CFDALoadCounterParty::ListType& myListin,
GenericClient& worker);
virtual ~GenericAlgorithm();

bool Process();
protected:

KbResultSetManager manager;
KbDbBinder binder;
vector<CExtRefSystemCode*> dave_list;
vector<CExtRefSystemCode*>::iterator dave_list_iterator;

private:

struct GenericAlgorithmImpl;
GenericAlgorithmImpl* pimpl_;


};


GenericClient.h
---------------

using namespace std;
#include "Generic.h"

class GenericClient
{
public:
virtual bool Filter( CExtRefSystemCode* );
virtual bool ProcessRow( CExtRefSystemCode* ) = 0;
virtual ~GenericClient() = 0 {};

protected:
KbResultSetManager manager;
KbDbBinder binder;
vector<CExtRefSystemCode*> dave_list;
vector<CExtRefSystemCode*>::iterator dave_list_iterator;

};


class MyWorker : public GenericClient
{
public:

bool Filter(CExtRefSystemCode*);
bool ProcessRow( CExtRefSystemCode*);
};



I have attempted to store the reference to GenericClient in the _pimpl

Generic.cpp
-----------
struct GenericAlgorithm::GenericAlgorithmImpl
{
public:
CFDALoadCounterParty::ListType m_list;
CFDALoadCounterParty::IterType m_iter;
GenericClient* pWorkerClient;
};


and have tried initialising it in the constructor:


GenericAlgorithm::GenericAlgorithm(CFDALoadCounterParty::ListType&
myListin, GenericClient& worker) : pimpl_( new GenericAlgorithmImpl()
)
{
pimpl_->m_list = myListin;
pimpl_->pWorkerClient = &worker;
}



The Client code constructs the GenericAlgorith object as follows

bool CFDALoadCounterParty::LoadIntoFDA()
{

GenericAlgorithm dave(m_list, MyWorker() );

dave.Process();

return true;

}

The problem occurs in the Process method when I try using the
pWorkerClient as it is pointing at a GenericClient object so when I
try calling

pimpl_->pWorkerClient->ProcessRow(*dave_list_iterator);

It fails internally as is tries calling a pure virtual function


I can kind of see where the problem lies but have been unable to make
the mental leap to fix it.

Any help greatly appreciated.

Many thanks in advance,
Dave.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Maxim Yegorushkin
Guest





PostPosted: Fri Jul 30, 2004 9:51 am    Post subject: Re: Pure Virtual Function call error Reply with quote



Dave <david.brooks (AT) drkw (DOT) com> wrote:

[]

Quote:
GenericClient.h
---------------

using namespace std;
#include "Generic.h"

class GenericClient
{
public:
virtual bool Filter( CExtRefSystemCode* );
virtual bool ProcessRow( CExtRefSystemCode* ) = 0;
virtual ~GenericClient() = 0 {};

protected:
KbResultSetManager manager;
KbDbBinder binder;
vector<CExtRefSystemCode*> dave_list;
vector<CExtRefSystemCode*>::iterator dave_list_iterator;

};


class MyWorker : public GenericClient
{
public:

bool Filter(CExtRefSystemCode*);
bool ProcessRow( CExtRefSystemCode*);
};

Please note, that MyWorker is still an abstract class that you can't instantiate, because it does not implement destructor which is defined as
pure virtual in GenericClient class.

[]

he Client code constructs the GenericAlgorith object as follows
Quote:

bool CFDALoadCounterParty::LoadIntoFDA()
{

GenericAlgorithm dave(m_list, MyWorker() );

dave.Process();

return true;

}

You are using a nonconformant compiler that allows you to instantiate abstract classes and bind r-values to non-const references (MSVC6?). What
happens is this:

GenericAlgorithm dave(m_list, MyWorker() );

here you instantiate (uninstantiable) temporary MyWorker instance which you pass to GenericAlgorithm which (illegally) binds it to a non-const
reference. After dave object has been constructed the temporary MyWorker instance is destroyed which destructor effectively reassigns its vptr
to the virtual table of the base class which is GenericClient. So, your dave object is left with a pointer to a nonexisting instance of MyWorker
whose vptr points to GenericClient vtable with pure virtual ProcessRow.

To fix the code implement the destructor in MyWorker and rewrite LoadIntoFDA as follows:

{
MyWorker wrkr;
GenericAlgorithm dave(m_list, wrkr);
dave.Process();
return true;
}

--
Maxim Yegorushkin

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Ulrich Eckhardt
Guest





PostPosted: Fri Jul 30, 2004 9:51 am    Post subject: Re: Pure Virtual Function call error Reply with quote



Dave wrote:
Quote:
class GenericClient;

class GenericAlgorithm
{
public:
GenericAlgorithm(CFDALoadCounterParty::ListType& myListin,
GenericClient& worker);
virtual ~GenericAlgorithm();
bool Process();
protected:
KbResultSetManager manager;
KbDbBinder binder;
vector<CExtRefSystemCode*> dave_list;
vector<CExtRefSystemCode*>::iterator dave_list_iterator;
private:
struct GenericAlgorithmImpl;
GenericAlgorithmImpl* pimpl_;
};

First thing, consider not using tabs for indenting, as it simply is not
portable(as the messed up layout shows...). Anyhow, there are already two
basic rules violated in this code:
1. law of three: implement ctor, cctor and assignment operator or none of
them. GenericAlgorithm objects are copyable as it stands.
2. virtual dtor: you have one, but that is useless since you don't have any
virtual methods. Your class is not means for derivation, right?

Quote:
GenericClient.h
using namespace std;

*niarg* Forcing all user of your class to also include std into their
global namespace...

Quote:
GenericAlgorithm::GenericAlgorithm(CFDALoadCounterParty::ListType&
myListin, GenericClient& worker) : pimpl_( new GenericAlgorithmImpl()
)
{
pimpl_->m_list = myListin;
pimpl_->pWorkerClient = &worker;
}

The Client code constructs the GenericAlgorith object as follows

bool CFDALoadCounterParty::LoadIntoFDA()
{
GenericAlgorithm dave(m_list, MyWorker() );
dave.Process();
return true;
}

*WoW* I really love those functions that always return true. That way you
never have to check their returnvalue. ;)

Seriously, What I wonder is whether 'MyWorker' is a type or a function. If
it's a type, I daresay you are not showing the code you are compiling.
Else, I wonder how long the object it returns will remain alive.

Quote:
The problem occurs in the Process method when I try using the
pWorkerClient as it is pointing at a GenericClient object so when I
try calling

pimpl_->pWorkerClient->ProcessRow(*dave_list_iterator);

It fails internally as is tries calling a pure virtual function

The pointer might be a 'GenericClient*', but that should not be the cause
of your problem as using it this way is the intended use for it and the
base of OOP in C++.
There are only two ways to call a pure virtual function erroneously: while
in the ctor and while in the dtor. In both cases, the object is not fully
constructed (or already partially destroyed), but AFAICT neither is the
case here.
Now, the only case left where I stumbled across this is when someone had a
dangling pointer, i.e. a pointer that once pointed to an object but that
object was already destroyed. Since you chose to disobey the rule of
three, you have to make sure manually that your classes are not misused;
I'd suggest fixing that first. Then, you can insert code that logs ctors
and dtors, and I'm sure you will find that you are operating on an object
that already ceased to exist. Also always be sure to turn on compiler
warnings.

Uli

--
Questions ?
see C++-FAQ Lite: http://parashift.com/c++-faq-lite/ first !


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Dave
Guest





PostPosted: Sat Jul 31, 2004 3:10 am    Post subject: Re: Pure Virtual Function call error Reply with quote

"Maxim Yegorushkin" <e-maxim (AT) yandex (DOT) ru> wrote

Quote:
Dave <david.brooks (AT) drkw (DOT) com> wrote:

[]

GenericClient.h
---------------

using namespace std;
#include "Generic.h"

class GenericClient
{
public:
virtual bool Filter( CExtRefSystemCode* );
virtual bool ProcessRow( CExtRefSystemCode* ) = 0;
virtual ~GenericClient() = 0 {};

protected:
KbResultSetManager manager;
KbDbBinder binder;
vector<CExtRefSystemCode*> dave_list;
vector<CExtRefSystemCode*>::iterator dave_list_iterator;

};


class MyWorker : public GenericClient
{
public:

bool Filter(CExtRefSystemCode*);
bool ProcessRow( CExtRefSystemCode*);
};

Please note, that MyWorker is still an abstract class that you can't instantiate, because it does not implement destructor which is defined as
pure virtual in GenericClient class.

[]

he Client code constructs the GenericAlgorith object as follows

bool CFDALoadCounterParty::LoadIntoFDA()
{

GenericAlgorithm dave(m_list, MyWorker() );

dave.Process();

return true;

}

You are using a nonconformant compiler that allows you to instantiate abstract classes and bind r-values to non-const references (MSVC6?). What
happens is this:

GenericAlgorithm dave(m_list, MyWorker() );

here you instantiate (uninstantiable) temporary MyWorker instance which you pass to GenericAlgorithm which (illegally) binds it to a non-const
reference. After dave object has been constructed the temporary MyWorker instance is destroyed which destructor effectively reassigns its vptr
to the virtual table of the base class which is GenericClient. So, your dave object is left with a pointer to a nonexisting instance of MyWorker
whose vptr points to GenericClient vtable with pure virtual ProcessRow.

To fix the code implement the destructor in MyWorker and rewrite LoadIntoFDA as follows:

{
MyWorker wrkr;
GenericAlgorithm dave(m_list, wrkr);
dave.Process();
return true;
}



Maxim,
many thanks for your comments. I did implement MyWorker methods so it
was no longer an abstract class.

After posting this I played around with it a bit more and came to the
solution you suggested - which fixed it. I then played around with it
some more and returned to the code in my original posting.

Believe it or not that magically worked ?????!!!!
Cheers,
Dave.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Olsen
Guest





PostPosted: Sun Aug 01, 2004 2:22 am    Post subject: Re: Pure Virtual Function call error Reply with quote

Your problem is that you are keeping a pointer to a temporary object,
and dereferencing that pointer after the temporary object has been
destroyed. That's undefined behavior, so anything could happen. In
your case, the symptom just happens to be a pure virtual function call.

Quote:
GenericAlgorithm::GenericAlgorithm(CFDALoadCounterParty::ListType&
myListin, GenericClient& worker) : pimpl_( new GenericAlgorithmImpl()
)
{
pimpl_->m_list = myListin;
pimpl_->pWorkerClient = &worker;
/* Storing a pointer to the second argument */


Quote:
}

bool CFDALoadCounterParty::LoadIntoFDA()
{
GenericAlgorithm dave(m_list, MyWorker() );
/* But the second argument is a temporary object, which is

destroyed at the end of the statement */

Quote:
dave.Process();
/* The pointer that was saved is dereferenced here, accessing

an object that has been destroyed. */

Quote:
return true;
}

--
David Olsen
[email]qg4h9ykc5m (AT) yahoo (DOT) com[/email]


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Olsen
Guest





PostPosted: Sun Aug 01, 2004 2:22 am    Post subject: Re: Pure Virtual Function call error Reply with quote

Maxim Yegorushkin wrote:

Quote:
Dave <david.brooks (AT) drkw (DOT) com> wrote:

GenericClient.h
---------------

using namespace std;
#include "Generic.h"

class GenericClient
{
public:
virtual bool Filter( CExtRefSystemCode* );
virtual bool ProcessRow( CExtRefSystemCode* ) = 0;
virtual ~GenericClient() = 0 {};
protected:
KbResultSetManager manager;
KbDbBinder binder;
vector<CExtRefSystemCode*> dave_list;
vector<CExtRefSystemCode*>::iterator dave_list_iterator;
};


class MyWorker : public GenericClient
{
public:

bool Filter(CExtRefSystemCode*);
bool ProcessRow( CExtRefSystemCode*);
};

Please note, that MyWorker is still an abstract class that you can't
instantiate, because it does not implement destructor which is
defined as pure virtual in GenericClient class.

MyWorker is not an abstract class. Since there is no user-defined
destructor, the compiler will automatically declare and define a
destructor for class MyWorker, just like it does for any other class.
This automatically-defined destructor overrides the pure virtual
destructor in GenericClient.

--
David Olsen
[email]qg4h9ykc5m (AT) yahoo (DOT) com[/email]


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Maxim Yegorushkin
Guest





PostPosted: Sun Aug 01, 2004 2:28 pm    Post subject: Re: Pure Virtual Function call error Reply with quote

David Olsen <qg4h9ykc5m (AT) yahoo (DOT) com> wrote:

Quote:
MyWorker is not an abstract class. Since there is no user-defined
destructor, the compiler will automatically declare and define a
destructor for class MyWorker, just like it does for any other class.
This automatically-defined destructor overrides the pure virtual
destructor in GenericClient.

Sorry, my mistake.

--
Maxim Yegorushkin

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.