Force virtual destructors? C++
Asked Answered
P

4

16

I didn't see the answer to this in the C++ Faq lite:

How do I define a base class so every class inheriting it is required to define a destructor?

I tried running this program

struct VDtor { virtual ~VDtor()=0;  };
struct Test:VDtor { virtual ~Test(){}  };
int main() { delete new Test; return 0; }

http://codepad.org/wFcE71w3 With the error

In function `Test::~Test()':
t.cpp:(.gnu.linkonce.t._ZN4TestD0Ev+0x1e): undefined reference to `VDtor::~VDtor()'
In function `Test::~Test()':
t.cpp:(.gnu.linkonce.t._ZN4TestD1Ev+0x1e): undefined reference to `VDtor::~VDtor()'

So, is it possible?

Polyhymnia answered 13/9, 2010 at 12:44 Comment(21)
+1 for what's probably the most space-efficient yet self-contained example code I've seen so far. :-DAdministration
Why ? What's the point of forcing the user to write more code ? If you have clean-up to do, do it yourself, don't put the burden on your user; if you don't, it's useless. Could you provide an example situation in which it would actually be useful ?Accommodate
@Matthieu M.: Code being generated passes only void* and childs are created then parents then random functions in random order to pass params/child to parents. Very messy. Every struct will receive a void* and i need to remember to clean it up. Maybe auto_ptr will solve it but i am unsure of side effects and half of the classes seem to be done.Polyhymnia
@acid I'm not sure about @Matthieu, but that makes absolutely no sense to me.Machmeter
Also there are existing if(ptr&& cond) which i know wont work if i change ptrs to use auto_ptr. I'll have to change a lot of lines if i do this and i risk side effects hence why forcing dtors seem to be the easiest non breaking solution. But maybe i should...Polyhymnia
Johannes Schaub - litb: I'll try again. There is C code being generated and functions are being called randomly. Also said functions pass in void*. I am left to decipher what type they may be and i essentially have no state so i need to create structs, add meta data and return them. Since i receive void* i dont really know what they are. At the end another function is called to 'parse' it all which i have my data and the generate data in correct order and can go through it. Basically i am saying its messy and i cant design in a traditional way.Polyhymnia
@acid oh deleting a void* can't be done. If you define destructors that won't change anything. The compiler doesn't magically know the type of what you delete from a void* just becaus it has a destructor defined. Have you tried this and it works?Machmeter
litb: Yes. All data is actually my data so i declare them all as VBase. I cant tell what the actual derive type is because the generated code uses void* and expects my prototype to use void*. I can deduce if its a char*, int or my data but thats all really.Polyhymnia
Come to think of it. Really the reason is 'i dont want to forget destroying pointers and all classes WILL be using void* by design.' I guess auto_ptr is the solution but then the code will look inconsistent and to make it consistent a lot of code will need to be changed + potential errors/side effects.Polyhymnia
@DevSolar: well, there’s still a redundant return 0; in the code …Homager
@acidzombie24: I am with litb here, you are confused: you can't use a auto_ptr<void*> and expect it to destroy the underlying type. You could use a auto_ptr<VBase> though, THAT would make sense.Accommodate
Matthieu M.: Yep. Thats what i would do if i use it. I asked around and apparently this WAS going to be a C app but we decided to change it to C++ and also i was told not to use auto_ptr because this code will be performance critical and because adding it will touch/change too many existing lines known to be good.Polyhymnia
@Konrad Rudolph: That would be pushing it a bit. main() is heralded to return an int, and so it should.Administration
@DevSolar: No. The C++ standard explicitly states that return 0; can be omitted from main. Don’t ask me why but that’s the way it is.Homager
@Konrad Rudolph: I know. But there's a fine line between "not necessary" and "butt-ugly if you don't do it". Implicit return 0 falls into the latter category IMHO.Administration
@DevSolar: of course you’re entitled to your opinion. In my book, however, “butt-ugly” is code that’s redundant, since the less code, the better.Homager
@Konrad Rudolph: Tell me you also consider comments to be redundant, and I'll get my twelve-gauge...Administration
@Konrad Rudolph: I could swear you MUST return a value in main (as all nonvoid functions) and main by standard MUST be int (and not void or anything else. But compilers dont enforce this).Polyhymnia
@DevSolar: well, if they are redundant, then yes, they are. And to be honest, most comments are. However, good comments are anything but redundant. They explain something completely different than the code, namely the why, not the how.Homager
@acidzombie24: No, you have it mixed up. The return type of main must be int and (only in C++) it must not be omitted but (only in C++) you don’t need the explicit return 0;.Homager
@Konrad: cool, good to know that.Polyhymnia
M
17

It is "possible" in some sense (if your goal was that the derived class stays abstract otherwise). But it won't give the result you would like: Because the compiler will create a destructor itself implicitly if the programmer hasn't done so.

It's therefor not possible to force the derived class' author to explicitly declare a constructor.

(edit: Like @chubsdad notes noted, the error in your particular code is because you need to define the explicitly declared destructor of the base class).


Edit: Just for fun, there are situations that necessiate an explicitly declared constructor. Consider the following

struct Viral {
  struct Dose { };
protected:
  ~Viral() throw (Dose) { }
};

struct Base {
  virtual ~Base() throw() { }
};

struct Derived : Base, Viral { };

This code won't compile because the implicitly declared ~Derived will have an exception specification throw (Dose) which is looser than what ~Base has - so it violates the requirement that overriders shall not have a looser exception specification. You will need to explicitly declare the destructor appropriately

struct Derived : Base, Viral { ~Derived() throw() { } };

But this is not really a solution to your problem, because derived classes need to "cooperate" into either deriving from Viral or putting it as a non-static data member. It's also very ugly :)


Edit: The following seems to be a Standard conforming way to do it

struct Viral {
  struct Dose { };
protected:
  ~Viral() throw (Dose) { }
};

struct Base : virtual Viral {
  virtual ~Base() throw() { }
};

Clang and GCC (starting with v4.6) reject any derived class of Base that has an implicitly declared destructor, because it has an incompatible exception specification (any derived class shall call ~Viral directly, instead of indirectly by calling ~Base, the Standard says). Comeau accepts this, but I strongly suspect that it is non-conforming in this regard.

Machmeter answered 13/9, 2010 at 12:51 Comment(8)
+1: I completely forgot about the implicit destructor while responding to this.Stickybeak
So does this mean No it isnt possible to force a derive class to define a dtor?Polyhymnia
@acid i would say it is not possible to force the programmer to explicitly define a destructor in a derived class. Can you please show what you need this for?Machmeter
i wrote a comment below the question.Polyhymnia
@Johannes Schaub - litb: Sorry, I deleted my post to avoid confusion on wrong information. Probably you can remove reference to my post alsoStickybeak
You should probably say No instead of "Yes it is possible" because my question is about forcing derive to define dtors which apparently is not possible.Polyhymnia
@acid i have adjusted my answer to be more clear. I see that it could have yielded to confusion.Machmeter
Johannes Schaub - litb: i see. Can you change your answer to more clearly reflect that? That was the reason why i havent accepted it. Because when i read it; to me it appeared you were saying i could forced derived classes to define it but i wont get results i want due to some implicitly created destructors which doesnt make sense bc why would they be generated if i am forced to define it. This only made sense after i read other answers/comments when i realize you meant yes force virtaul destructors which is what i asked in the title but not in main body which i thought you were answering.Polyhymnia
C
1

Every class has a destructor, regardless. Declaring a virtual destructor in the base ensures that children will have virtual destructors. This doesn't mean that the coder will need to explicitly declare a destructor -- that wouldn't be a good thing, anyhow. All it means is that, if a destructor is declared, it will be virtual.

Choker answered 13/9, 2010 at 13:5 Comment(4)
Declaring the base's dtor to be virtual also means the implicitly declared dtor in any derived class will be virtual. Which, as you say, is all you need.Giggle
@Fred: Yes, I should have been more clear in explaining that even implicitly declared destructors in child classes would always be virtual. Once virtual, always virtual. Thanks for pointing out my lack of clarity here.Choker
Looking at it again, "if a destructor is declared" is where I saw the problem: a dtor is always declared, either by the programmer or implicitly by the compiler. (This seems much more clear than my previous comment.)Giggle
@Fred: Yes, I should have said "explicitly declared" in the last sentence of my answer.Choker
P
1
struct VDtor { virtual ~VDtor()=0;  };
VDtor::~VDtor () { } // <== Implementation.
struct Test:VDtor { ~Test(){}  };
int main() { delete new Test; return 0; }

To fix the error you have to actually implement the VDtor::~VDtor() like above.

Patmos answered 13/9, 2010 at 14:40 Comment(2)
But that doesnt force derive class to implement their own destructor.Polyhymnia
@acidzombie24: Either a class needs non-empty dtor and then nothing the base can do will help or the implicit and empty dtor is good enough and then there is no point in spelling it out explicitly, IMHO.Patmos
M
0

When Test is destructed, it will call it's base class destructor, which doesn't exist. You should just declare it empty if you have no necessary destruction logic.

Melar answered 13/9, 2010 at 13:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.