3

I was told on this forum, I have read it in the books, and there are other answers stating that it is never good to inherit STL containers, because of the STL container destructors are not virtual (undefined behavior when deleting a derived object via base class pointer).

However, the C++11 standard now allows a design-by-contract checking at compile time, with member function specifiers, like delete. I can fully understand why inheriting an STL container with a goal to extend with one member function or two is better replaced with coding the member functions as algorithms. Still, I have a situation where I model a collection of elements as a Concept, and the elements themselves are containers of other elements. I need to implement bidirectional iterators that access sub-element data Forward Iterator (e.g. Collection::sub_element_iterator). Using composition forces me in this case, to re-type (adapt) the entire std::vector public interface, only to extend this new Collection with an iterator.

Is in this scenario o.k if I still use inheritance, and prevent heap allocation in the following way?

Here is a small model of this:

#include <vector>

class not_heap_allocable
{
    public: 
        void* operator new  (std::size_t count) = delete;
};

template
<
    typename Type,
    template <typename T> class Allocator = std::allocator
>
class extended_vector
:
    public std::vector<Type, Allocator<Type>>, 
    public not_heap_allocable
{
    public: 
        typedef std::vector<Type> BaseType; 

        using BaseType::BaseType; 
};

using namespace std;

int main(int argc, const char *argv[])
{

    vector<int>* vNew = new extended_vector<int> ({0,1,2,3,4,5});  // Compile time error. 

    return 0;
}

Which then results in a compile-time error:

main.cpp: In function ‘int main(int, const char**)’:
main.cpp:31:64: error: use of deleted function ‘static void* not_heap_allocable::operator new(std::size_t)’
     vector<int>* vNew = new extended_vector<int> ({0,1,2,3,4,5});  // Compile time error. 
                                                                ^
main.cpp:6:15: error: declared here
         void* operator new  (std::size_t count) = delete;

As a result, the extended_vector is no longer counting on humans not to misuse it.

Community
  • 1
  • 1
tmaric
  • 5,347
  • 4
  • 42
  • 75
  • 1
    I guess you want to inherit from `std::vector`, not `std::vector`? – leemes Feb 05 '14 at 16:05
  • I can't help, but that smells. – πάντα ῥεῖ Feb 05 '14 at 16:10
  • Forbidding heap allocation doesn't express the intent clearly IMHO (with a non-virtual dtor, it's still valid to create an object of the derived class on the heap, you just may not delete it through a base class pointer). Another possibility is to use `private` (or `protected`) inheritance and re-publish the member functions via `using`-declarations. – dyp Feb 05 '14 at 16:13
  • `not_heap_allocable` forbids also *placement new*... Note also that `extended_vector` is not necessary on the stack (if it is part on an other class). – Jarod42 Feb 05 '14 at 16:19
  • Can you show us an example of what you want to do with that two-level container? It sounds like you could provide an iterator class as a namespace-scope (not nested) class, with free functions to initialize the iterator objects. – dyp Feb 05 '14 at 16:20
  • Deriving from classes with non-virtual destructors is not an issue if the derived destructor does exactly the same as the base one (usually because you didn't add any extra member). – Marc Glisse Feb 05 '14 at 16:44
  • @dyp: Example: put simply, I can't. The best I can do is to tell you I have something like std::vector> and I want to have an element_iterator as a bidirectional iterator in the top container. regarding your comment: can you share more details on this approach? can I find an example somewhere online? – tmaric Feb 05 '14 at 17:44
  • @MarcGlisse: that is actually what I thought in the first place - i'm using inheritance and not introducing attributes only to be able to have type-based distinction between std::vector> and Collection>, if I am to use nested containers. – tmaric Feb 05 '14 at 17:46

2 Answers2

2

This approach seems more complicated and possibly less clear than inheriting protected and then using (into public) the base class members your child class actually needs to expose. That prevents all the problems of publicly inheriting in such a way while not introducing any unexpected concerns by probiting new.

EDIT: I found what I was looking for from boost: This SO question Writing an iterator that makes several containers look as one which links to boost::join http://www.boost.org/doc/libs/1_46_1/libs/range/doc/html/range/reference/utilities/join.html which lets you join multiple ranges into one.

Community
  • 1
  • 1
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Can you give me more details on more complicated and possibly less clear? I mean, I understand the danger here, but isn't it more complicated to re-introduce the majority of the public interface, since I want the Collection to behave exactly like `vector` for Elements? +1 the adapter, let me know if you find it :) – tmaric Feb 05 '14 at 17:51
  • Thanks for the link to the iterator, but actually this answer: http://stackoverflow.com/a/6748024/735756 is more similar to what I need. A collection of collections, not two "adjacent" containers. – tmaric Feb 06 '14 at 08:52
0

No it does not make sense to derive from classes not designed to have derivations.

You actually have to encapsulate the class and forward each desired functionality to that encapsulated member.

Since the derived class is still a base you will loose derived as soon as it passed as base.

You might use private inheritance, but still have to expose each functionality again.