Microsoft runtime error R6025 Pure Virtual Function Call: a technicalreport

D

Dario

This is a technical C++ post regarding the
Microsoft runtime error R6025 Pure Virtual Function Call
that sometime occurs in programs compiled with
Microsoft Visual C++ 6.0.

Please consider the following simple illegal C++ program:

class Listener {
public:
virtual void onEvent(int n) = 0;
};
class A : public Listener {
public:
Listener * listener;
int val;
explicit A(int n) {listener = this; val = n;}
virtual ~A() {}
virtual void run() {listener->onEvent(val);}
};
int main() {
A(42).run();
return 0;
}

This program is illegal because in the main() function
there is a call to A constructor but A cannot be constructed
because it does not declare the onEvent function.

When you try to compile it using g++
you correctly obtaing the following error:
virtual.cpp: In function `int main ()':
virtual.cpp:14: cannot allocate an object of type `A'
virtual.cpp:14: since the following virtual functions are abstract:
virtual.cpp:3: void Listener::eek:nEvent (int)

But, surprise!, when you try to compile it using
the Microsoft C++ 6.0 compiler you have no error.
Only when you execute it you will obtain the infamous
Microsoft runtime error R6025 Pure Virtual Function Call.

If your application sometime suffers of R6025 error
there are two manners to discover the problem:
<1> Try to compile the program using an
alternative correct c++ compiler (like g++),
-or-
<2> Make a code inspection on the program
and try to figure out all code containg the pattern:
className(someParam).methodInvocation(otherParam)
and substitute it with:
className xxx(someParam);
xxx.methodInvocation(otherParam)

If you try <2> on the previous simple illegal C++ program
you will obtain a main() like this:
int main() {
A xxx(42);
xxx.run();
return 0;
}
and now, when you compile it with using the Microsoft C++ compiler
you will correctly obtain the error:
error C2259: 'A' : cannot instantiate abstract class ...

Short Story
===========

IMHO a C++ program error is masked by a bug
of the Microsoft C++ 6.0 compiler.
Accurate code inspection -or- using an alternative
correct C++ compiler (like g++)
may help in finding the error.

- Dario
 
R

Ron Natalie

I don't know why you feel comp.lang.c++ cares about Microsnot compiler bugs.
This program is illegal because in the main() function
there is a call to A constructor but A cannot be constructed
because it does not declare the onEvent function.

Absolutely incorect. You can not call constructors. What you have
is an attempt to create a temporary object initialzed with 42. But you
are right in that A is still abstract and the program is ill-formed when it tries
to create an A object.
 
A

Alf P. Steinbach

I don't know why you feel comp.lang.c++ cares about Microsnot compiler bugs.

"Microsoft", not a deragatory variant.

Many here care because they're in the position of using actual compilers
to produce actual code.

The more you know about compiler errors like that, the easier it is to
conclude whether some error is a language violation or a compiler error
(in the old days that was obvious, 'cause a compiler was almost never
wrong, but with modern C++ it's another story).


Absolutely incorect. You can not call constructors.

There's enough people on this group discussing basic _terminology_ if not
old-timers too. But for the record:

a constructor can be called.

C++ merely couples that with object allocation, as does all other languages
I know (since that's the point of a constructor), so:

a constructor for type T cannot be called on an existing type T object ...

.... by using conventional syntax, and it cannot be so called at all in a well-
defined way (using placement new that way is, I think, undefined behavior,
unless the existing object is destroyed first via explicit call of destructor).
 
K

Kanon Wood

Comments below.

Alf P. Steinbach said:
bugs.

"Microsoft", not a deragatory variant.

Many here care because they're in the position of using actual compilers
to produce actual code.

The more you know about compiler errors like that, the easier it is to
conclude whether some error is a language violation or a compiler error
(in the old days that was obvious, 'cause a compiler was almost never
wrong, but with modern C++ it's another story).




There's enough people on this group discussing basic _terminology_ if not
old-timers too. But for the record:

a constructor can be called.

C++ merely couples that with object allocation, as does all other languages
I know (since that's the point of a constructor), so:

a constructor for type T cannot be called on an existing type T object ....

... by using conventional syntax, and it cannot be so called at all in a well-
defined way (using placement new that way is, I think, undefined behavior,
unless the existing object is destroyed first via explicit call of destructor).

The example at the bottom allocates enough memory for 10 CMyObj's. It uses
the following statements to construct and destruct the objects as needed.

m_aData.CBankT::CBankT();
m_aData.CBankT::~CBankT();


Is this a compiler loophole, or valid code?

-Kanon



// Creates a static block of memory
// that can hold several T objects.
template <class T>
class CBank_t
{
class CBankT : public T {};

CBankT * m_aData;
bool * m_aInUse;

int m_iCapacity;
int m_iSize;

public:
CBank_t(int iCapacity)
{
// allocate memory for future use
m_aData = (CBankT*)malloc(iCapacity*sizeof(CBankT));
m_aInUse = new bool[iCapacity];
memset(m_aInUse, 0, sizeof(bool)*iCapacity);

m_iSize = 0;
m_iCapacity = iCapacity;
}

T * Create()
{
int i = 0;
// Find an object not in use
while (i < m_iCapacity && m_aInUse)
i++;

if (i >= m_iCapacity)
return NULL;

m_aData.CBankT::CBankT();
m_aInUse = true;
m_iSize++;

return &(m_aData);
}

void Release(T * pObj)
{
int i = pObj - m_aData;

if (i < m_iCapacity)
{
m_aData.CBankT::~CBankT();
m_aInUse = false;
m_iSize--;
}
}

int GetSize()
{return m_iSize;}

int GetCapacity()
{return m_iCapacity;}
};

// Simple do nothing class
class CMyObj
{
int m_iX;
public:
CMyObj()
{m_iX = 0;}
~CMyObj()
{m_iX = -1;}

void SetX(int x)
{m_iX = x;}

int GetX()
{return m_iX;}

};


int main()
{
CBank_t <CMyObj> theBank(10);

CMyObj * pObj1, *pObj2;

pObj1 = theBank.Create();
pObj2 = theBank.Create();

pObj1->SetX(3);
pObj1->GetX();

pObj2->SetX(5);
pObj2->GetX();

theBank.Release(pObj1);
theBank.Release(pObj2);

return 0;
}
 
A

Alf P. Steinbach

The example at the bottom allocates enough memory for 10 CMyObj's. It uses
the following statements to construct and destruct the objects as needed.

m_aData.CBankT::CBankT();

Is this a compiler loophole, or valid code?


That's a compiler loophole _if_ any compiler accepts it. More
specifically it's a compiler error. To construct objects in-place use
placement new (look it up).


m_aData.CBankT::~CBankT();


This is technically OK, I think (never used that feature, so
I'd have to check the syntax ;-) ).

That does not mean it's OK at any other level.



// Creates a static block of memory
// that can hold several T objects.
template <class T>
class CBank_t
{
class CBankT : public T {};

CBankT * m_aData;
bool * m_aInUse;

int m_iCapacity;
int m_iSize;

public:
CBank_t(int iCapacity)
{
// allocate memory for future use
m_aData = (CBankT*)malloc(iCapacity*sizeof(CBankT));


Ouch.


Hth.,

- Alf
 
K

Kanon Wood

Alf P. Steinbach said:
The example at the bottom allocates enough memory for 10 CMyObj's. It uses
the following statements to construct and destruct the objects as needed.

m_aData.CBankT::CBankT();

Is this a compiler loophole, or valid code?


That's a compiler loophole _if_ any compiler accepts it. More
specifically it's a compiler error. To construct objects in-place use
placement new (look it up).


VC 6.0 compiles it OK, but per your advice I replaced it with placement new.
Works fine.
m_aData.CBankT::~CBankT();


This is technically OK, I think (never used that feature, so
I'd have to check the syntax ;-) ).

That does not mean it's OK at any other level.


MSDN says it's OK to use with placement new.


How would you suggest allocating this memory without constructing objects?
Hth.,

Hth?

- Alf

-Kanon
 
K

Kevin Goodsell

Alf said:
m_aData.CBankT::~CBankT();



This is technically OK, I think (never used that feature, so
I'd have to check the syntax ;-) ).


I didn't look at the rest of the code very carefully, but I think
usually the syntax is

object->~Object();

(because usually you are dealing with a pointer). I can't see an obvious
problem with calling it directly on the object, but I don't know about
the explicit qualification.

-Kevin
 
A

Alf P. Steinbach

How would you suggest allocating this memory without constructing objects?

I wouldn't... ;-)

As is so often the case in this group, the real problem is most probably
something else - when you find that you "need" something as ugly as that,
there is something very very wrong with the approach (it just makes things
infinitely worse that Bruce Eckel has such an example in his online book).

But anyway, in the hypotethical case (science fiction) where something like
that is really needed you might consider a std::vector<char> as buffer.


Btw., please don't crosspost to Microsoft newgroups, because that may drag
Microsoft-specific responses into [comp.lang.c++].

FUT: [comp.lang.c++].
 
C

Craig Powers

Dario said:
This is a technical C++ post regarding the
Microsoft runtime error R6025 Pure Virtual Function Call
that sometime occurs in programs compiled with
Microsoft Visual C++ 6.0.

FWIW, VC7.1 (aka VC.NET 2003) correctly identifies the error.
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top