View Full Version : Debug Error! R6025 - pure virtual function call
Nemesis
06-29-2004, 01:02 PM
A question for the C++ gurus... I'm getting this run-time exception after some substantial playtesting.
Has anyone encountered such an error and any idea of the likely problem?
freeman
06-29-2004, 01:20 PM
Hopefully this will help, it's from msdn.microsoft.com
C Run-Time Error R6025
pure virtual function call
No object has been instantiated to handle the pure virtual function call.
This error is caused by calling a virtual function in an abstract base class through a pointer which is created by a cast to the type of the derived class, but is actually a pointer to the base class. This can occur when casting from a void* to a pointer to a class when the void* was created during the construction of the base class.
Wayward
06-29-2004, 01:22 PM
More details in PRB: Cause of the R6025 Run-Time Error (http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q125/7/49.asp&NoWebContent=1)
Nemesis
06-29-2004, 01:26 PM
Thanks.. I've actually seen that explanation but found it too cryptic!
I'm hoping someone has actually experienced the problem and can relate the likely cause and solution in simpler terms :)
(edit) Was referring to freeman's post. @Wayward: thanks for the post too.. that's very helpful!
Cheers guys! :)
Ronkes
06-30-2004, 01:00 AM
If I understand MSDN's error description correctly, it goes something like this.
The following base class contains an abstract virtual function.
class Base {
public:
virtual void foo() = 0;
};
This means that you cannot instantiate the base class directly. You have to create a derived class.
class Derived : public Base {
public:
void foo() {}
};
int main() {
Base base; // compiler error, Base is an abstract class
Derived derived; // no problem, since derived implements foo()
}
As you can see, the compiler catches the error. The problem arises when you pass a pointer to a Base-object around, assume it is a pointer to a Derived object and call foo() on it. At this point you might wonder how you can pass a Base-object around, since you cannot instantiate Base. Enter the constructor.
When you create an object of type Derived, the object is constructed in two steps. First the constructor for Base gets called. Second, the constructor for Derived gets called. And then you have your object.
Within the Base constructor, the object you are creating is not yet of type derived, but only of type Base. If you pass a pointer to the object around from within Base (risky), you are passing a pointer to a Base-object.
class Base;
void bar(Base*);
class Base {
public:
virtual void foo() = 0;
Base() { bar(this); }
};
class Derived : public Base {
public:
void foo() {}
};
void bar(Base* base) {
base->foo();
}
Normally, the code in bar() is fine. Since you cannot instantiate a Base-object, the passed pointer must point to a class derived from Base. Except in this case, through some C++ voodoo, you managed to pass an object of type Base anyway and that object doesn't have an implementation for the function foo().
Look through your code and try to find a constructor where you are passing the this-pointer around, either within the constructor's body or in the initialization list (even riskier). Chances are that your problem stems from there.
Hope that helps.
Matthijs Hollemans
06-30-2004, 01:52 AM
You may also have done an unsafe cast somewhere. That would be something like:
((Base*) ptr)->foo();
Where ptr really isn't a member of Base.
Note that these problems can remain undetected on Windows 98. Like many other kinds of errors, they only show up on WinNT and up.
Ronkes
06-30-2004, 02:56 AM
Where ptr really isn't a member of Base.
I take it you mean derived class instead of member.
Obviously the cast would be an error, but I just tried and I get an access violation instead of an R6025.
An R6025 does occur when you do the same thing I describe above, but in the destructor instead of in the constructor.
Matthijs Hollemans
06-30-2004, 03:07 AM
I take it you mean derived class instead of member.
Whoops, indeed :).
I only mentioned this particular case since it bit me in the butt once. My WinXP box was down for repair and I only had 98 to test on. After having released the update to my program, people complained that the program apparently crashed for no reason on 2000 and XP. Turns out that I was doing a bad cast and consequently called a pure virtual function. Win98 doesn't really care about that (even though the bad call may mess up memory and you won't find out until much later). This is why C++ has dynamic_cast<> and a few others :).
Nemesis
06-30-2004, 05:59 AM
Guys,
Thanks for your feedback.. this forum is indeed a treasure trove :)
The problem seems to be due to a custom memory manger I developed for the game. It basically supports pooled object allocations with fallback calls to malloc / free when the pool gets full. To cut a long story short.. with a few #defines I can fit any class within the memory manager so that calls to new and delete are automatically overridden.
Anyway.. I had this one particular class (spreadshot projectiles) for which I defined a pool of 64 instances. The instance count was however peaking at 117 on all my test runs.
Well.. no problem there as the memory manager falls back to unpooled allocations.. however.. it seems I have a bug somewhere in the fallback mechanism with the result that some objects are being corrupted.. with the vf table reference being one of the first victims :) Hence, the reason why the pure function references of the abstract base class were being used.
Note: In case you're thinking about object size mismatches, pools are allocated per class. Also I'm specifically avoiding having dervied classes that use the pool of their parent class by assigning them to a pool of their own i.e. this prevents pool memory corruption by derived object classes that are larger than their parents.
In closing, careful when you mess with Mother Nature or memory allocation code!
(edit) To give you an idea of the MM have a look here (http://colinvella.no-ip.com/perihelion/042.jpg). I must admit I'm pretty happy with it! :) The screenshot shows the drop-down console with the output of a memory status display command. You may notice the hierarchichal heap structure. Items marked with [P] signify pooled objects. The rest are allocated on an ad-hoc basis.