Sign in to follow this  
Sc4Freak

"Extending" STL containers?

Recommended Posts

Let's say that I've got a std::vector. But, I want to add some additional functions to it (in other words, "extending" std::vector). That is, I want a class that is still a std::vector, but just with some extra helper functions for example. I was wondering, how would I go about doing this in C++? One possibility would be to use composition. That would allow me to provide a wrapper around std::vector, and still provide the helper functions. But then my class wouldn't "be-a" std::vector anymore; it would "have-a" std::vector. Same goes for private inheritance. As I understand it, public inheritance is for "is-a" relationships. But public inheritance won't work for STL containers: they don't have virtual destructors. So deleting the derived class from a base class pointer will be bad news. The fact that the STL containers don't have virtual destructors is (very) probably by design, which means that I'm also probably not supposed to publicly derive from them (and by extension: also means that I can't have an "is-a" relationship with an STL container?). Of course, I could just do:
void HelperFunction(std::vector& Foo);
That would work fine, but it'd be nicer if it could be a member function. Is it possible, or is my thinking getting a bit muddy?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
One possibility would be to use composition. That would allow me to provide a wrapper around std::vector, and still provide the helper functions. But then my class wouldn't "be-a" std::vector anymore; it would "have-a" std::vector. Same goes for private inheritance.


Doesn't matter. The interface is a generic one - if you provide the traits it requires, that is good enough. Public inheritance provides an "is-a" relationship, but it is sufficient, not necessary - nothing dictates that 2 classes without a public inheritance relationship cannot still have an "is-a" relationship.

If you were to copy and paste the std::vector class, rename it to Sc4FreakVector, it would still 'be' a vector, for the purposes of the standard library's algorithms and so on. If you then added extra functionality, again it would still 'be' a vector, providing you didn't remove any of the interface or break any algorithmic guarantees that vectors give you. And even if you did, it would probably still 'be' a valid container.

Share this post


Link to post
Share on other sites
Also, the lack of a virtual destructor doesn't automatically mean you can't use public inheritance. If you know what you're doing, it's still possible. In your case, it sounds like you just want to add new functions? In that case, there's no good reason you can't use public inheritance. Even if you're adding members with simple types, you're still okay. The only real problem is if you need to add non-trivial code to the destructor.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
Of course, I could just do:
void HelperFunction(std::vector& Foo);

That would work fine, but it'd be nicer if it could be a member function.

Is it possible, or is my thinking getting a bit muddy?


Why do you think it would be nicer to add this functionality as a member function? I would actually argue that using a free function is better design, it is more encapsulated and has less coupling that way.

Look at the STL algorithms, they are all implemented as free functions. In fact if you code your helper function to take pointers or iterators to begin and end, like the STL algorithms, you're functionality will be much more general.

I would discourage against inheriting from std::vector even if you know that you won't need the subclass to have a destructor run. The standard library classes should be treated as primitives by your code, and other programmers will likely find it confusing and hackish.

That's just my opinion though.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
Of course, I could just do:
void HelperFunction(std::vector& Foo);

That would work fine, but it'd be nicer if it could be a member function.
No it wouldn't. Only the primitive operations of the object that need access to its internals need to be members. If HelperFunction can be defined in terms of existing public methods, it should be a non-member.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
Let's say that I've got a std::vector. But, I want to add some additional functions to it (in other words, "extending" std::vector). That is, I want a class that is still a std::vector, but just with some extra helper functions for example. I was wondering, how would I go about doing this in C++?

One possibility would be to use composition. That would allow me to provide a wrapper around std::vector, and still provide the helper functions. But then my class wouldn't "be-a" std::vector anymore; it would "have-a" std::vector. Same goes for private inheritance.

As I understand it, public inheritance is for "is-a" relationships. But public inheritance won't work for STL containers: they don't have virtual destructors. So deleting the derived class from a base class pointer will be bad news.


There is nothing wrong with publicly deriving from a standard library container as a base class. In fact it is a very good idea under some circumstances.

By publicly deriving from such a base class you get all the functionality of, say, the vector, but you have a distinct class that can be used for function overload resolution or template deductions. That is not the case if you simply use a typedef of the container.

Here's an example:

class Base64Buffer: public std::vector<char>
{
public:
Base64Buffer(const char*, const char*);

std::vector<char> toBinary() const;
};

inline bool operator==(const Base64Buffer& lhs, const Base64Buffer& rhs)
{
return lhs == rhs;
}

inline bool operator==(const Base64Buffer& lhs, const std::vector<char>& rhs)
{
return lhs.toBinary() == rhs;
}

inline bool operator==(const std::vector<char>& lhs, const Base64Buffer& rhs)
{
return lhs == rhs.toBinary();
}


The caveat is that you cannot treat such a type polymorphically. Don't use pointers or references to your derived class as pointers or references to std::vector.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this