I have an abstract class in my dll.
class IBase { protected: virtual ~IBase() = 0; public: virtual void f() = 0; };
I want to get IBase
in my exe-file which loads dll. First way is to create following function
IBase * CreateInterface();
and to add the virtual function Release()
in IBase
.
Second way is to create another function
boost::shared_ptr<IBase> CreateInterface();
and no Release()
function is needed.
Questions.
1) Is it true that the destructor and memory deallocation is called in the dll (not in exe-file) in the second case?
2) Does the second case work well if exe-file and dll was compiled with different compilers (or different settings).
So, we should use shared_ptr when we want to assign one raw pointer to multiple owners. // referring to the same managed object. When to use shared_ptr? Use shared_ptr if you want to share ownership of a resource.
In short: Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.
A shared_ptr may share ownership of an object while storing a pointer to another object. get() returns the stored pointer, not the managed pointer.
shared_ptr are noticeably slower than raw pointers. That's why they should only be used if you actually need shared ownership semantics. Otherwise, there are several other smart pointer types available. scoped_ptr and auto_ptr (C++03) or unique_ptr (C++0x) both have their uses.
An answer to your first question: The virtual destructor in your dll is called - the information about its location is embedded in your object (in the vtable). In the case of memory deallocation it depends how disciplined the users of your IBase
are. If they know they have to call Release()
and consider that exception can bypass the control flow in an surprising direction, the right one will be used.
But if CreateInterface()
returns shared_ptr<IBase>
it can bind the right deallocation function right to this smart pointer. Your library may look like this:
Destroy(IBase* p) { ... // whatever is needed to delete your object in the right way } boost::shared_ptr<IBase> CreateInterface() { IBase *p = new MyConcreteBase(...); ... return shared_ptr<IBase>(p, Destroy); // bind Destroy() to the shared_ptr } // which is called instead of a plain // delete
Thus every user of your DLL is easily prevented against resource leaks. They never have to bother about calling Release()
or pay attention to exceptions bypassing surprisingly their control flow.
To answer your second question: The downside of this approach is clearly stated by the other answers: You're audience has to use the same compiler, linker, settings, libraries as you. And if they could be quite a lot this can be major drawback for your library. You have to choose: Safety vs. larger audience
But there's a possible loophole: Use shared_ptr<IBase>
in your application, i.e.
{ shared_ptr<IBase> p(CreateInterface(), DestroyFromLibrary); ... func(); ... }
Thus no implementation specific object is passed across the DLL boundary. Nevertheless your pointer is safely hidden behind the shared_ptr
, who's calling DestroyFromLibrary
at the right time even if func()
's throwing an exception or not.
I would advise against using shared_ptr
in the interface. Even using C++ at all in the interface of a DLL (as opposed to "extern C" only routines) is problematic because name-mangling will prevent you from using the DLL with a different compiler. Using shared_ptr
is particularly problematic because, as you have already identified, there is no guarantee that the client of the DLL will use the same implementation of shared_ptr
as the caller. (This is because shared_ptr
is a template class and the implementation is contained entirely in the header file.)
To answer your specific questions:
I'm not quite sure what you are asking here... I'm assuming that your DLL will contain implementations of classes derived from IBase
. The code for their destructors (as well as the rest of the code) will, in both of your cases, be contained in the DLL. However, if the client initiates the destruction of the object (by calling delete
in the first case or by letting the last instance of the shared_ptr
go out of scope in the second case), then the destructor will be called from client code.
Name-mangling will usually prevent your DLL from being used with a different compiler anyway... but the implementation of shared_ptr
may change even in a new release of the same compiler, and that can get you into trouble. I would shy away from using the second option.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With