C.35: Why should a base class destructor be virtual, if the class doesn't have virtual functions?
Concerning "C.35: A base class destructor should be either public and virtual, or protected and nonvirtual". I suspect this recommendation only makes sense for polymorphic hierarchies, which should be explicitly stated in the item.
As an example, consider cv::Scalar class from OpenCV. It derives from cv::Vec class, which makes sense, since a scalar essentially is a vector, with a fixed number of dimensions (four). But both classes can be owned, thus both have public destructors. Which are non-virtual, since there are no virtual functions in the whole hierarchy. Furthermore, binary layout of the classes is often considered to have exactly the data members, and vtable would be definitely not convenient. Both classes are mostly used as values, you can rarely see cv::Vec*, where you can place an instance of cv::Scalar.
I'm not saying this design is perfect, but it seems reasonable. Should we declare it bad? Should we say it violates the guidelines and should be avoided?
Just want to mention that in the section the link mentions types with virtual functions:
C.35: A base class with a virtual function needs a virtual destructor
while the item itself never mentions this which may be confusing.
@toughengineer this is probably an issue with the fix of #375.
Agreed, thanks.
@hsutter thanks for the fix, but it does not address original problem. Please reopen.
OK, reopened to discuss with editors. Note that by "base class" we mean a class designed to be used as a base class where calling code passes by pointer/reference and uses derived objects as-a base. That implies the inheritance is interface inheritance as opposed to implementation inheritance.
In the OpenCV example, why does Scalar derive from Vec -- is it so that code can use a Scalar where a Vec is expected, or is it "implementation inheritance" to reuse implementation?
Note that by "base class" we mean
I cannot find this definition in the guidelines. Is this some sort of common knowledge?
In the OpenCV example, why does Scalar derive from Vec -- is it so that code can use a Scalar where a Vec is expected, or is it "implementation inheritance" to reuse implementation?
I'm quite sure this is "implementation inheritance" to reuse implementation.
I think that this rult C.35 is not clear enough. A lot of people shure that virtual destructor is required when class has a virtual functions but it isn't right. The real reason is a "base class" definition while "has virtual functions" is only sufficient condition. I would like to add some extra explanations in reason section to clarify the cases:
Reason To prevent undefined behavior. The destructor is used to clenup members, including members of derived classes. If the destructor is public, then calling code can attempt to destroy a derived class object through a base class pointer, and the result is undefined if the base class’s destructor is non-virtual. If the destructor is protected, then calling code cannot destroy through a base class pointer and the destructor does not need to be virtual; it does need to be protected, not private, so that derived destructors can invoke it. This rule is applied on each level of class hierarchy. So, each intermediate level should define the class destruction rule. In general, the writer of a base class does not know the appropriate action to be done upon destruction.
I'm not saying this design is perfect, but it seems reasonable. Should we declare it bad? Should we say it violates the guidelines and should be avoided?
I think it is a bad design exacty due to C.35 violation. It allows to delete the Scalar class by pointers to it's base classes and could be a source of errors. The design expects the binary match of classes in heirarchy that could be broken at any level.
The desgin fix for code reusing is in moving of common part into separate base class (VecBase ?) with protected destructor and inherit it by Vec : public VecBase and Scalar pbulic VecBase with public destructors and final restriction.
The desgin fix for code reusing is in moving of common part into separate base class (VecBase ?) with protected destructor and inherit it by Vec : public VecBase and Scalar pbulic VecBase with public destructors and final restriction.
Can't stress this enough. It is so easy to follow C.35 without ifs, buts, maybes. Qt uses this a lot too.
The only problem I've encountered with this is to teach people that they should accept a const VecBase& and not const Vec& if they don't explicilt require a Vec specifically.
Qt uses this a lot too.
Qt does mostly interfaces as they use PIMPL so even if you directly derive, it's still an interface kind of thing as you don't have direct access to the actual implementation - nearly all the implementation is in a private internal class object with the public API a wrapper around it to expose it, which gives them strong ABI compatibility.