Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using shared_ptr in dll-interfaces

Tags:

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).

like image 470
Alexey Malistov Avatar asked Oct 22 '09 07:10

Alexey Malistov


People also ask

When should you use shared_ptr?

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.

Why would you choose shared_ptr instead of Unique_ptr?

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.

What does shared_ptr get () do?

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.

Is shared_ptr slow?

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.


2 Answers

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.

like image 200
phlipsy Avatar answered Sep 19 '22 15:09

phlipsy


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:

  1. 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.

  2. 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.

like image 38
Martin B Avatar answered Sep 18 '22 15:09

Martin B