So I have been using a container derived from std::vector for some time. Perhaps this is a poor design decision for several reasons, and the question of whether or not you should do such a thing has be discussed extensively here:
Thou shalt not inherit from std::vector
Subclass/inherit standard containers?
Is there any real risk to deriving from the C++ STL containers?
Is it okay to inherit implementation from STL containers, rather than delegate?
I am sure I have missed some of the discussions…but reasonable arguments for both viewpoints are found in the links. As far as I can tell, the "because ~vector() is non-virtual" is the foundation for the "rule" that you shouldn't inherit from stl containers. However, if I look at the implementation for std::vector in g++ 4.9.2, I find that std::vector inherits from _Vector_base, and _Vector_base a non-virtual destructor.
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
...
~vector() _GLIBCXX_NOEXCEPT
{ std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
_M_get_Tp_allocator()); }
...
}
where:
template<typename _Tp, typename _Alloc>
struct _Vector_base
{
...
~_Vector_base() _GLIBCXX_NOEXCEPT
{ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage
- this->_M_impl._M_start); }
...
}
So the gcc 4.9.2 implementation of std::vector inherits from base classes with a non-virtual destructor. This leads me to believe that it is an acceptable practice. Why is this OK? What are the specific conditions by which such a practice is not dangerous?
std::vector
does not inherit from _Vector_base
Or rather, not in my standard library implementation. libc++ implements vector like so:
namespace std
{
template <class T, class Allocator = allocator<T> >
class vector
{
...
Sure, your implementation might inherit from a base class, but my implementation does not. This leads to the first error of your interpretation of the "rule":
The C++ standard library can be implemented in a variety of ways, and we really can't make broad, sweeping assumptions or statements about the standard library (the one defined by ISO/IEC 14882:2014
) from one implementation of it.
So, before we get any further, let's remember: we're not going to focus on a single implementation in this answer. We're going to consider all implementations (by focusing on following the definitions in the C++ standard; not the definitions of a particular header file on your hard drive).
std::vector
inherits from _Vector_base
But before we continue, let's admit that your implementation might inherit from _Vector_base
, and that's okay. So why is std::vector
allowed to inherit from a base class, but you aren't allowed to inherit from std::vector
?
std::vector
, if you want (just be careful)std::vector
can inherit from _Vector_base
for the same reasons you can inherit from std::vector
: the library implementors are very careful (and you should be too).
std::vector
is not delete
d polymorphically with a _Vector_base
pointer. So we don't have to worry about ~vector()
not being virtual. If you don't delete
your inherited class with a polymorphic std::vector
pointer, then ~vector()
being non-virtual is a non-issue. It's fine. No worries. Let's not fuss about it. No polymorphic delete
s means we don't have to worry about destructors being virtual or not.
But beyond this, the library implementors have ensured that std::vector
is never sliced from using a _Vector_base
reference. Same for you: if you can ensure your custom vector class is never sliced (by someone using a std::vector
reference to it), then you're fine here too. No slicing means we don't have to worry about bad copies being made.
std::vector
This has been answered a lot in other questions, but I'll say it again here (and with the focus of the whole _Vector_base
inheritance issue): you (probably) shouldn't inherit from std::vector
.
The problem is that if you do, someone using your custom vector class might polymorphically delete it (they might have a std::vector
pointer to it, and delete
it, which is A Bad Thing if they do). Or they might have a std::vector
reference to your custom object and try to make a copy of it (which would slice the object, which would also probably be A Bad Thing) (I keep assuming a std::vector
reference to your custom object is needed to cause object slicing when copying, because I keep assuming that you're careful enough to never accidentally slice an object with a careless assignment or function call; you would never be so careless, I'm sure, but someone else with a std::vector
reference might be (and yes, I'm being a little facetious here)).
You can control what you do with your code. And if you can control it (carefully) enough to ensure there are no polymorphic delete
s or object slicing, you're fine.
But sometimes you can't really control what others do with your code. If you're working on a team, this might be problematic if one team member unwittingly does one of these things. Which is why I keep bringing up the "be careful" thing.
Even worse, if your code is used by a client, you really can't control what they do, and if they do one of these bad things, you're the one who is probably going to be blamed and tasked with fixing it (have fun refactoring all your code that used to rely on your custom class inheriting from std::vector
) (or just tell the client they can't do that, but you'll probably have a grumpy client who wasted time debugging a weird issue they didn't expect to run into).
Your C++ standard library implementers can get away with this inheritance, though, because they can control things very well: no one is allowed to use _Vector_base
. You can use std::vector
. Only std::vector
. Since you're not allowed to ever (ever) use _Vector_base
, the standard library implementers don't have to worry about object slicing or polymorphic delete
s. And since they're being very careful in their implementation in a controlled environment, things work out just fine.
But even better, by not making assumptions about how std::vector
is implemented, and instead treating it like a (useful) black box, we can make sure that how we use std::vector
is portable to other standard library implementations. If you make assumptions about std::vector
inheriting from some base class, you're going to be limiting the portability of your code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With