As written:
From the Google C++ style guide, which serves as the base for
Chromium's style:
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Interfaces
"To make sure all implementations of the interface can be destroyed
correctly, the interface must also declare a virtual destructor (in an
exception to the first rule, this should not be pure). See Stroustrup,
The C++ Programming Language, 3rd edition, section 12.4 for details"
(This is also a reference to Stroustrup, 12.6.9, "A class with a
virtual function should have a virtual destructor §12.4.2")
Proposal:
An exemption, detailed in the Chromium style guide at
http://dev.chromium.org/developers/coding-style , based on
http://www.gotw.ca/publications/mill18.htm / "H. Sutter, More
Exceptional C++, Item 27, p168"
"A base class destructor should be either public and virtual, or
protected and nonvirtual."
Context:
Throughout the Chromium codebase, we tend to have many "Delegate" and
"Interface" type classes that declare a series of pure virtual
functions that must be implemented by derived classes. These Delegates
are then passed to some concrete class, as the way of having the lower
level and higher level code communicate, via Dependency Injection.
Many times, the lifetimes of these Delegates are defined as "Must
outlive the class they're passed to". Alternatively, and subjectively
less commonly, they're defined as "This class takes ownership of the
Delegate", with the concrete class then storing the class in
scoped_ptr<Delegate>
Reason in favor:
For situations where there is no particular ownership requirement for
the Delegate - that is, it outlives whatever class or set of classes
its passed to - I would propose that we should allow explicit,
protected, non-virtual destructors to be declared. For situations
where ownership is retained or intended to be retainable, the
destructor should be explicit, public, and virtual.
The context for this is I have been working to ensure that any
RefCounted/RefCountedThreadSafe derived class does not have an
accessible public destructor, as that can lead to double frees. In a
number of places, there are ref-counted classes implementing some
FooInterface. FooInterface may have an implicit (compiler-generated)
public destructor, or it may have a public, virtual destructor, even
though ownership of FooInterface is never taken. Occasionally, they
are protected and virtual.
By permitting protected, non-virtual destructors on these Interface
classes, we allow them to be safely implemented by ref-countable
classes, without worrying about accidental double deleting by way of
deleting via the FooInterface* (explicitly, scoped_ptr, DeleteSoon,
etc). Further, with Clang, we can then make it a compile error if any
(base) class in the ref-counted implementation has a public
destructor.
Reason against:
It's been pointed out that by ensuring Interfaces have explicit,
public, virtual destructors, it becomes easier to do dependency
injection. The class that expects an Interface can then reliably store
ownership of the Interface, if they wish, ensuring that the Interface
is always a valid pointer while the class that uses it is valid, and,
once the class that uses it goes out of scope, that the Interface is
always cleaned up.
Rather than multiply-inheriting, RefCounted classes that wish to
implement FooInterface would instead need to use the adapter pattern
to glue the Interface implementation to the ref-counted implementation
that performs the actual work. For some, this offers a better
solution, in that it resolves ambiguities regarding inheritance chains
and encourages more composition. See
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Multiple_Inheritance#Multiple_Inheritance
and
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Inheritance#Inheritance
for more on Composition-vs-Inheritance preferences.