Coming to C++ from a different C-With-Objects environment, I got bit by the following.
Example output:
Parent::A(construct) this: 1245052, vtbl: 4219068 Parent::B(construct) this: 1245052, vtbl: 4219068 Child::A(methodic ) this: 1245052, vtbl: 4219056 Child::B(methodic ) this: 1245052, vtbl: 4219056 Parent::A(Parent::A) this: 1245052, vtbl: 4219056 Child::B(Parent::A) this: 1245052, vtbl: 4219056 Parent::A(destruct ) this: 1245052, vtbl: 4219068 Parent::B(destruct ) this: 1245052, vtbl: 4219068
/* example of pure virtual function call, and constructor/destructor modification of vtable ptr. constructors and destructors will only call virtual methods defined in that _specific_ class. */ #include <stdio.h> void report(const char *msg1, const char *msg2, void *thisp) { printf("%s(%s) this: %ld, vtbl: %ld\n", msg1, msg2, thisp, *(void **)thisp); } class Parent { public: Parent() { A("construct"); } virtual ~Parent() { A("destruct "); } virtual void A(const char *what) { report("Parent::A", what, this); B(what);
// this will cause a 'pure virtual function called' // runtime error when Parent() calls A() calls C() = 0 // C("boom");
} virtual void B(const char *what) { report("Parent::B", what, this); } virtual void C(const char *what) = 0; }; class Child : public Parent { public: virtual void A(const char *what) { report(" Child::A", what, this); B(what); Parent::A("Parent::A"); } virtual void B(const char *what) { report(" Child::B", what, this); } virtual void C(const char *what) { report(" Child::C", what, this); } }; void main(void) { Child c; c.A("methodic "); }
Yes, this standard-conforming behavior: During the constructor and destructor of the parent class, the instance *IS* of the parent class. This means that any calls to virtual functions from a parent constructor or destructor will call the parent's functions or crash -- you can't get polymorphic dispatch to child virtual methods.
Microsoft C++ has a flag to disable this behavior, as a performance optimization, but using it violates the language standard. -- JeffGrigg
The reason, by the way, is that a child virtual may access instance members that may not have been constructed or already have been destroyed. Thus, you cannot call a child virtual. Java behaves similarly. In Smalltalk, instead of super new initialize, you may write super new foo initialize (i.e. send foo before initialize), but if foo depends on some side effects of initialize (and it will if foo is interesting), then you will probably regret it. -- SunirShah
P.S. In all likelihood, Parent::~Parent() should be virtual. If you ever delete *pParent even if *pParent is a Child, you'll die in horrible flames.
(Yup, that destructor should be virtual, I changed the code for good form. I had thought that a problem only arises if a child class actually implements a destructor, that it won't get called correctly, but Meyers says that the behaviour of deleting a base-pointer that is really a pointer-to-child, without a virtual destructor, is undefined.)
Once I understoood what was going on, and why, it makes sense. It was just a surprise initially - and it's not well documented. I think Stroustrup's 2nd edition had a line or two discussing this, the 3rd edition has a paragraph. C++ FAQs, ISBN 0-201-30983-1 actually covers this pretty well (FAQ 20.13 & 20.14), though I haven't seen anywhere that it mentions how you can actually get this PureVirtualFunctionCalled error to occur.
They also mention that C++ and Java work differently here, that 'many people don't think this rule is intuitively obvious', and it comes down to a difference between reference semantics and value semantics.
I've actually seen this error message pop up in an older version of NetScape. Another programmer said 'it must not be C++ - that can't happen in C++.'
Java behaves similarly.
No, Java does not behave similarly.
class A { public A() { System.out.println("A()"); f(); } public void f() { System.out.println("A.f()"); } } class B extends A { public B() { super(); System.out.println("B()"); } public void f() { System.out.println("B.f()"); } } public class Test { public static void main(String[] args) { A a = new B(); } }Output on Java 1.3.1 on Windows:
A() B.f() B()
I HaveThatBug?. I've been bitten twice (shame on me!) with Thread classes. The Thread class has a virtual Run method which is overrided by a derived class in order to do work in a separate thread. In standard ConstructionIsAcquisition? form, the Thread destructor waits for the running thread to exit. This works fine until the Run method of the derived class calls a virtual method or uses some instance variable of the derived class.
In the worst case, the virtual method exists only in the derived class, the vtable pointer is changed by the Thread destructor, and the virtual method dispatch jumps through a garbage pointer. Tracking this down, eventually getting to the level of distrusting the vtable pointer in the object, and discovering that it really is changing, leads to a sinking feeling - first "the world has gone haywire", then "oh, the language really does say that, doesn't it - phooey!".
The solution is to have the destructor of every derived class wait for the thread to exit. Yuck! -- AnonymousDonor
Nah, you just didn't think hard enough. You should have delete this from within the thread. See the library (although it may not compile by itself--if so, e-mail me) http://sunir.org/c2/WriteYourOwnThreadingPackage/Threading.zip -- SunirShah
Yeah, it sounds like those constructors/destructors ought to be protected (static class::make() and instance->destroy() instead of public class() and ~class() ). No doubt Sunir's library does something like that - but the link isn't live (yet?).
Fixed. I had FTPed it to sunir.org/c2/Threading.zip, but it's now in the right place. Sorry about that. --ss
The library posted doesn't do that as you aren't supposed to subclass from CThread (well, you are--note the virtual ~CThread()). However, if I were to do that, I would definitely use a NamedConstructor and protect the constructor and destructor. I believe this will get you where you want to go:
class CRunnable : public CThread { public: template<typename T> static bool Spawn() { CRunnable *pRunnable = new T; return pRunnable->Start(pRunnable); } protected: CRunnable() : CThread( ThreadMain?, true, TerminateCallback?, this ) {} virtual void Run() = 0; virtual void Terminated() {} static void ThreadMain?( void *pCopyOfThis ) { ((CSubclassThread *)pCopyOfThis)->Run(); } static void TerminateCallback?( void *pCopyOfThis ) { ((CSubclassThread *)pCopyOfThis)->Terminated(); } private: }; class CSubclass : public CRunnable { public: ... protected: virtual void Run();// interesting stuff goes here virtual void Terminated(); // optional private: }; CRunnable::Spawn<CSubclass>();However, you could also write as per the CBaz example:
class CRunnable { public: virtual bool Start() { return (new CThread(ThreadMain?, true)->Start(this, true); } template<typename T> static bool Spawn { CRunnable *pRunnable = new T; // Forces T to be a subclass of CRunnable. return pRunnable->Start(); } protected: CRunnable(); virtual void Run() = 0; virtual void Terminated() {} static void ThreadMain?( void *pCopyOfThis ) { ((CSubclassThread *)pCopyOfThis)->Run(); } static void TerminateCallback?( void *pCopyOfThis ) { ((CSubclassThread *)pCopyOfThis)->Terminated(); } private: }; class CSubclass : public CRunnable { ... }; CRunnable::Spawn<CSubclass>(); (new CSubclass)->Start();I prefer the latter as templates are evil. -- SunirShah