Oleksii wrote:
Is this a feature of compiler(s) being too smart (even when all
optimizations are disabled) or is it an expected behavior? What exactly
is happening here? If this is compiler-specific I assume it is not very
scalable and I'd better drop using it. Any other suggestions in that
case?
Here is some sample code:
#include <memory>
#include <iostream>
class MyClass2
{
public:
MyClass2() {};
virtual ~MyClass2();
};
int main()
{
// Uncommenting the following line would lead to complaint
// from linker that requires implementation for ~MyClass2().
// std::auto_ptr<MyClass2> myClass2r (new MyClass2);
std::auto_ptr<MyClass2> myClass2n;
}
p.s. I'm working on a rather big legacy code thing and therefore a bit
limited in redesign. Link-time stubbing for test purposes seems to be
the easiest approach.
Hi! First I want to say that I'm 99% sure that the standard does not
in any way force a system to link this without crying about the missing
"virtual ~MyClass2". But I can tell you why it works since it's really
very basic.
Let's say we have a pointer called "p" which is of type "MyClass2*".
If "MyClass2" has a non-virtual dtor, then "delete p;" will directly
invoke "MyClass2::~MyClass2()". Whether or not "p" is NULL is only
known at runtime, and so the linker will cry about
"MyClass2::~MyClass2()" if it can't be found.
If "MyClass2" has a virtual dtor, then "delete p;" will call the dtor
of "MyClass2" or the dtor of a class derived from "MyClass2" if "p"
happens to point to such a derived class. Now, that could be done by
many different means of reflection, but the way it is usually done is
by using a virtual-function-table (short: vtable). For this to work
the compiler places a pointer to that vtable inside every instance of
"MyClass2". Now to call a virtual function on "MyClass2" the compiler
looks up the vtable by using that vtable pointer stored somewhere
"inside MyClass2", and then look up the function-address in the vtable.
Then it just calls the function at that address, whatever it might be.
So in short, the "delete p" thingy does no longer have to know the
address of "MyClass2::~MyClass2()". If "p" points to a "MyClass2"
the dtor slot in the vtable will point to "MyClass2::~MyClass2()",
but if "p" points to some derived class that slot will point to
dtor of that derived class.
Now, the compiler only has to know the address of the vtable when
initializing a new instance of "MyClass2" since this is the point where
it will store the vtable's address "inside" the "MyClass2" instance.
So, to sum it up: the ctor code of "MyClass2" will take the address of
"MyClass2"'s vtable, and the vtable will reference the dtor. Since the
ctor of "MyClass2" is not called by your program the linker will not
link it. And since the ctor is the only part that directly references
the vtable of "MyClass2" the vtable will also not be linked, and since
the vtable of "MyClass2" is the only part that directly references the
dtor of "MyClass2" the dtor will also not be linked.
So it's not about a compiler being too smart, but about a linker that
does it's job as it's supposed to do it.
Look at this for example:
class MyClass2
{
public:
MyClass2() {};
// with ~MyClass2 being virtual the call to MyClass2::MyClass2
// in main() will cause a linker error, since the compiler has
// to create the vtable for MyClass2 which contains a reference
// to ~MyClass2. If you try the same with a non-virtual ~MyClass2
// the program will probably link fine (at least with MSVC7.1
// it definately does) since the dtor is never being called,
// and since it's not virtual anymore it's also not being
// referenced.
virtual ~MyClass2();
};
int main()
{
int space[100];
reinterpret_cast<MyClass2*>(&space)->MyClass2::MyClass2();
}