473,729 Members | 2,331 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

[Q] Virtual destructors and linking

Hello,

I'm rather new to the advanced topics, therefore I cannot explain the
following myself. Could anyone give me a hint on this one?

I'm trying to avoid link-time dependencies on (a test version of)
certain classes. In other words I don't want to link stuff that I don't
use. One thing that worked for me was replacing instance members
(MyClass myClass) within a class with auto_ptr (auto_ptr<MyCla ss>
myClass) and initializing it with a new instance of a class in the
product code and a NULL in the test code.

This works... unless the class being auto_ptr'ed has a non-virtual
destructor. The linker is happily eating input with virtual
destructors. Otherwise the implementation of the destructor is claimed
to be missing.

The whole thing was originally tested on M$ visual studio compiler,
then on gcc (cygwin/3.4.4).

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<M yClass2> myClass2r (new MyClass2);

std::auto_ptr<M yClass2> 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.

Thanks in advance, Oleksii.

Jul 23 '05 #1
7 2205
Oleksii wrote:
Hello,

I'm rather new to the advanced topics, therefore I cannot explain the
following myself. Could anyone give me a hint on this one?


You're lucky it works even without a virtual destructor.
You effectively use the destructor in either case.

Jul 23 '05 #2
Oleksii wrote:
I'm rather new to the advanced topics, therefore I cannot explain the
following myself. Could anyone give me a hint on this one?

I'm trying to avoid link-time dependencies on (a test version of)
certain classes. In other words I don't want to link stuff that I don't
use. One thing that worked for me was replacing instance members
(MyClass myClass) within a class with auto_ptr (auto_ptr<MyCla ss>
myClass) and initializing it with a new instance of a class in the
product code and a NULL in the test code.
But that's definitely not the same... An instance of a class requires
different code to call members than an auto_ptr...
This works... unless the class being auto_ptr'ed has a non-virtual
destructor. The linker is happily eating input with virtual
destructors. Otherwise the implementation of the destructor is claimed
to be missing.

The whole thing was originally tested on M$ visual studio compiler,
then on gcc (cygwin/3.4.4).

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();
So, why don't you just write

virtual ~MyClass2() {}

The same empty parentheses don't seem to cause you any trouble in the
[unneeded] default constructor...
};

int main()
{
// Uncommenting the following line would lead to complaint
// from linker that requires implementation for ~MyClass2().
// std::auto_ptr<M yClass2> myClass2r (new MyClass2);
Of course it requires the implementation of ~MyClass2() -- it uses it!
The destructor for 'auto_ptr' actually destroys the object by calling
'delete' on the point it "owns".
std::auto_ptr<M yClass2> myClass2n;
I am not sure how the compiler could be that smart, but probably it can.

Here you never actually create an instance of 'MyClass', so it never needs
to destroy it. I am guessing that the clever implementation of
std::auto_ptr somehow knows whether the object has or hasn't been
constructed and produces a special destructor in the case where only the
default constructor was used. Optimizing compiler might be able to do
something like that...
}

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.


What exactly are your limitations? Can't you give your classes empty
(doing nothing) destructors?

V
Jul 23 '05 #3
First of all you're right, in the implementation I have to change all
myClass.DoSomeh ting() into myClass->DoSomething( ), but I consider it as
a minor change. I assume that was the difference you have mentioned. I
don't have to add anything though - no additional work for destructors,
since smart pointers do everything under the hood.

Regarding my limitations. I cannot change design of the classes, but I
can tweak classes a bit (e.g. replacing instances with pointers). The
problem with changing design is that the code is not testable
standalone - it is a huge monolithic piece of sw.
(Ideally) I don't want to link/target all libraries I use in my
production code, but only the necessary ones (at the moment I have to
copy 18(!) DLLs to the test folder to be able to instantiate my class
in test environment...) . When I've used auto_ptr's I only have to
include the definitions of the classes, but I don't have to link/target
the implementations (DLLs) along!

That is also the reason why I don't provide any empty implementations
for destructors - they do have they full-blown implementations in a
separate DLL, but I don't want to link/stub it because in principle it
is never used.

The reason why I come up with the question is that I don't understand
how is it possible that this works :) and how scalable it is
(compiler-specific).

I hope this makes it clearer.

oleksii

Jul 23 '05 #4
Ron Natalie wrote:
I'm rather new to the advanced topics, therefore I cannot explain the
following myself. Could anyone give me a hint on this one?


You're lucky it works even without a virtual destructor.
You effectively use the destructor in either case.


Yep, I also feel that. And therefore before using it in the production
code I want to understand why is it working and not throwing any
exceptions (e.g. when auto_ptr is trying to determine which destructor
has to be called). What I think now is that might be a smart
implementation of the delete operator (not the operator delete if I'm
correct), that does not bind to the class implementation since NULL
pointer is provided. Just a thought.

I've looked through the auto_ptr implementation and I don't see
anything very special about this case.

Nevertheless the question remains: why this damn thing works with
virtual constructors and complains when there is no virtual
constructor...

oleksii

Jul 23 '05 #5
Oleksii wrote:
[..]
The reason why I come up with the question is that I don't understand
how is it possible that this works :) and how scalable it is
(compiler-specific).


I am not sure, but I have a gut feeling that it's compiler-specific.
You should take a look at the implementation of the 'auto_ptr' class
template. Perhaps the compiler/library employs some kind of indirect
construction and since the other (underlying) template is not used
(and therefore not instantiated) in the case when you only use the
default c-tor for 'auto_ptr', the code to call the destructor is not
there (not instantiated), and the linker doesn't need it. Now, why
the things are different between virtual/non-virtual destructors, I
don't know. Hard to imagine how it would be implemented. Then again,
I am not a compiler expert, perhaps there is something that your
compiler does behind the scenes, which it cannot do if the destructor
is non-virtual...

V
Jul 23 '05 #6
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<M yClass2> myClass2r (new MyClass2);

std::auto_ptr<M yClass2> 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::~MyC lass2()". Whether or not "p" is NULL is only
known at runtime, and so the linker will cry about
"MyClass2::~MyC lass2()" 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::~MyC lass2()". If "p" points to a "MyClass2"
the dtor slot in the vtable will point to "MyClass2::~MyC lass2()",
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::MyCla ss2
// 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_cas t<MyClass2*>(&s pace)->MyClass2::MyCl ass2();
}
Jul 23 '05 #7
Thanks a lot for your post! What I missed myself is the part that the
object was never actually created (constructor is not linked at all). I
only have focused on the destruction totally ignoring the construction
part.

Now things became clear. I assume any well-written linker should behave
in this manner, otherwise a lot of unused classes would be linked
together, which is unwanted behavior.

Thanks!

oleksii

Jul 23 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
4550
by: Chunhui Han | last post by:
Hi, I was recently reading about virtual base classes in C++. The book I was reading says that it is illegal to have non-virtual destructor for the virtual base class. It seems to me that virtual destructors are essential when the class has virtual functions, since this class is supposed to be used as a base class for polymorphism. But what is the real reason for a virtual destructor in a virtual base class? Thanks,
7
1888
by: qazmlp | last post by:
When a member function is declared as virtual in the base class, the derived class versions of it are always treated as virtual. I am just wondering, why the same concept was not used for the destructors. What I am expecting is, if the destructor is declared as virtual in base, the destructors of all its derived classes also should be virtual always. What exactly is the reason for not having it so?
11
2315
by: Bonj | last post by:
Hello, Can anyone help me with these fairly simple questions. 1) What is the point in virtual destructors - I've heard it's a good thing for base-classes, but what are the advantages and disadvantages of doing so / or not as the case maybe? 2) In the following example, are code sections BD and DD *both* going to get called? I assume they both always will, but is this guaranteed beyond all odds in all cases? (When I say is it...
5
2349
by: Ruben Campos | last post by:
I've recently noticed that it's not allowed to call a pure (non-implemented) virtual method inside a constructor or a destructor, doesn't matter if this method is declared in the considered class itself or in one of its base classes. Such an attempt results in a linker undefined symbol error. Why? Is it right, or is it a bad issue of my compiler/linker (I'm working with Microsoft Visual C++ 7.1.3088)? Thank you in advance for your help.
11
4365
by: santosh | last post by:
Hello, I was going through the Marshal Cline's C++ FAQ-Lite. I have a doubt regarding section 33.10. Here he is declaring a pure virtual destructor in the base class. And again defining it inline. Like this.
4
1264
by: Zeng | last post by:
I often run into situation where I would like to have a method such as that has derived behavior similar to destructors in c++, is that possible? public BaseClass { private int m_dataInBase; public BaseClass() {m_dataInBase = 0} public void SetDataBase( int val ) { m_data = val; } virtual public void ClearData()
5
11359
by: druberego | last post by:
I read google and tried to find the solution myself. YES I do know that you can get undefined references if you: a) forget to implement the code for a prototype/header file item, or b) you forget to pass all the necessary object files to the linker. Neither of those are my problem. Please bear with me as the question I ask is rather long and I think it's beyond a CS101 level of linker stupidity. If it is a stupid CS101 mistake I'm making...
9
2044
by: desktop | last post by:
On this page: http://www.eptacom.net/pubblicazioni/pub_eng/mdisp.html Shape specify the virtual function: virtual double Intersect( const Shape& s) = 0; then the derived class Circle also specify:
1
613
by: Stephen Horne | last post by:
On Fri, 10 Oct 2008 13:42:57 -0700 (PDT), James Kanze <james.kanze@gmail.comwrote: There are issues with some compilers, but probably only old ones. I remember problems with Borland C++ 5.02. Each DLL ended up with a different heap. It's possible that linker options or similar were to blame, but the easiest thing at the time was to ensure that data always got deleted by the same DLL that newed it - usually not a big
0
8761
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9426
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
9200
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9142
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
1
6722
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6022
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4525
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4795
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
2680
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.