2

I'm using framework which has a class Register, into which I'm able to register instances of A.

Register r;
A * a1 = new A();
r->register(a1);
A * a2 = new A();
r->register(a2);

Register will take ownership and deletes all registered As when going out of scope.

I want to modify As behaviour in shared library (.so in my case), so, I'm gonna do something like this (in the library):

class B : public A {
    ...
}

B * get_customized_a() {
    return new B();
}

And then in main program

Register r;
A * a1 = get_customized_a();
r->register(a1);

but now a1 will be deleted in main program, not in the library! Which from I understud is a big no-no.

So how to solve this?


I've come up with two solution:

1) Use A and customize it by stand-alone function

in plugin:

void customize_a(A * a) { ... }

in main program:

Register r;
A * a1 = new A();
customize_a(a1);
r->register(a1);

I must say I don't like it that much :/

2) Overload delete operator for class B

in plugin

class B : public A {
    ...
    static void operator delete(void * ptr) {
        ::operator delete(ptr);
    }
}

in main program:

Register r;
A * a1 = get_customized_a();
r->register(a1);

However, I never overloaded operator delete before so I'm not sure if this will even work (as intended).

3) Is there any approach I missed? Is there a better solution?

Thank you all.

graywolf
  • 7,092
  • 7
  • 53
  • 77
  • 2
    Does `A` have a virtual dtor? Also, on which platform are you? – Deduplicator Oct 04 '14 at 18:50
  • No, if I'm reading source correctly, `A` doesn't have virtual dtor. Platform is Linux, AMD64 – graywolf Oct 04 '14 at 18:56
  • If it's Linux, you really don't have to take any special care about module-boundaries. Deleting through a base-type without virtual dtor stays UB though. – Deduplicator Oct 04 '14 at 18:58
  • Sooo... on linux I can safely in main program delete something allocated in shared library? Is this in-practice-it-works kind of behaviour, or it does work always? – graywolf Oct 04 '14 at 19:04
  • And any thoughts how to handle the absence of virtual dtor ? :/ – graywolf Oct 04 '14 at 19:04
  • @Paladin - `And any thoughts how to handle the absence of virtual dtor?` Add one to the base class. – PaulMcKenzie Oct 04 '14 at 19:10
  • Of course it is only it works by default, and you can break it if you want. If you override global `new` and `delete`, take a look at the final so/executable: Does it really properly export them (noticed a bug lately: http://stackoverflow.com/q/25922895)? Regarding the missing virtual on the dtor, fix your code. Or play roulette. – Deduplicator Oct 04 '14 at 19:10
  • @Deduplicator @PaulMcKenzie that's the problem, `A` is not my code :/ I cannot just change function signature :/ So basically I'm screwed? :D – graywolf Oct 04 '14 at 19:13
  • You are screwed, though how much ... If you don't add any members in the derived class, and don't add a custom dtor, it should work even though it's UB. No guarantees there. – Deduplicator Oct 04 '14 at 19:15
  • @Paladin - Well, if the class doesn't have a virtual destructor, then maybe it was never meant to be derived from. Does `A` have any virtual functions? If so, then immediately contact the author to add a virtual destructor. That amounts IMO to a bug, even though the code will compile. If there are no virtual functions, then again, the class was probably not designed to be derived from. – PaulMcKenzie Oct 04 '14 at 19:19
  • @PaulMcKenzie yes, it does have virtual functions. Plus inheriting from it is recommended way to use it according to documentation. I'll ask why dtor is not virtual. – graywolf Oct 04 '14 at 19:44
  • @Paladin - If what you say is true, then honestly, it is an oversight by the author. The class should have a virtual destructor, otherwise it truly can't be used polymorphically without at some point invoking UB. You certainly can't use it if you're creating derived objects dynamically, but destroying the objects using a pointer to the base class. – PaulMcKenzie Oct 04 '14 at 19:47
  • @Paladin - Just to make sure, is `A` derived from any other class? If so, does the class that `A` is derived from have a virtual destructor? In other words, is there any virtual destructor defined in any parent class of `A`? – PaulMcKenzie Oct 04 '14 at 19:49
  • Oh, stupid me :/ I didn't check that. Yes, `A`'s base does have virtual dtor. So I can just let the `Register` delete it even when it was allocated in shared library and it should work just fine on linux.. Thank you sirs :) – graywolf Oct 04 '14 at 20:49

1 Answers1

0

If you delete a class by using a pointer to one of the base classes then the destructor has to be virtual in the base class regardless of the location of object creation and destruction. Module boundaries have nothing to do with this.

On the other hand if the construction and destruction of an object happen in different modules it is a problem only if the new and delete operator of these modules allocate/deallocate memory from different pools. This can be the case for example if these modules are linked against different versions of the runtime library. A similar case is when you explicitly override the new and delete operators in the libraries and do the allocation yourself from different pools without cooperation between the allocator code of these modules. In this case a virtual destructor doesn't save you from the problems because the construction of the object allocates memory using the allocator/pool of one module, and you call the delete operator of another module that tries to deallocate the object from a completely different pool. The result is usually a false "heap corruption" runtime error or something similar.

If you ever face the above problem then a correct solution is introducing both a virtual destructor and a virtual Release() method in the base class. The Release() method should delete the object with an ugly delete this;. When you pass the constructed object to a foreign module then it has to delete the object by calling the Release() method instead of directly deleting it and this way the Release() method deletes the object with the delete operator of the module that owns the class and the instance. Often this technique is coupled with smart pointers so that the foreign module references such objects with smart pointers that automatically delete the objects by calling the Release() method.

pasztorpisti
  • 3,760
  • 1
  • 16
  • 26
  • This doesn't really help me. I cannot change the way objects are hold (== cannot switch to smart pointers) or deleted (== cannot enforce deleting via `Release()`), I'm not in control of that code. – graywolf Oct 05 '14 at 14:26
  • @Paladin I've written this answer just to clarify things. Your problem has many good solutions and my post doesn't narrow down the path into a single golden pattern to follow. If your base class contains a virtual dtor then you don't have to change anything in it given that both modules allocate/deallocate from the same heap. That solution doesn't confront with the statements in my post. – pasztorpisti Oct 05 '14 at 16:40