Jump to content
  • Advertisement
Sign in to follow this  
Lode

inheriting from std::vector

This topic is 4428 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to make this class that's inherited from std::vector in C++. However, it gives an error that I don't understand. For some reason, it doesn't want me to use "size()", while size really is part of the class I inherited from and public, so it really should be accessible without strange error!
#include <vector>

template<typename T>
class pvector : public std::vector<T>
{
     public:
     ~pvector() { for(int i = 0; i < size(); i++) delete this; }
};

/*
errors:

pvector.h: In destructor 'pvector<T>::~pvector()':
pvector.h:7: error: there are no arguments to 'size' that depend on a template parameter, so a declaration of 'size' must be available
pvector.h:7: error: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
*/


I don't plan on using -fpermissive. What's wrong and how do I fix it? Thanks.

Share this post


Link to post
Share on other sites
Advertisement

#include <vector>

template<typename T>
class pvector : public std::vector<T>
{
typedef pvector<T> this_type;
public:
~pvector() { for(int i = 0; i < this_type::size(); i++) delete this_type::operator[](i); }
};



You must specify the concrete type of the vector who's methods you want to invoke, since both size() and operator[] lack the required template arguments as the destructor is declared without having one (or something along the lines of that[lol]).

HTH,
Pat.

Share this post


Link to post
Share on other sites
std::vector does not have a virtual destructor, which is a good indication that you shouldn'y be trying to derive from it. Consider instead making a std::vector a member variable of your class.

Share this post


Link to post
Share on other sites
cool, it works now, it also works with (*this)[] instead of this_type::operator[](i), really strange, I wonder why it works for operator[] but not for size()

Also, I don't know if this_type is something that exists for some compilers, but it didn't exist in mine so I used std::vector<T>::size()

Thanks to this I can do proper OO now without having to delete stuff myself and without boost :)

#ifndef pvector_h
#define pvector_h

#include <vector>

template<typename T>
class pvector : public std::vector<T>
{
public:
~pvector() { for(int i = 0; i < std::vector<T>::size(); i++) delete (*this); }
};

#endif

Share this post


Link to post
Share on other sites
Quote:
Original post by Lode
cool, it works now, it also works with (*this)[] instead of this_type::operator[](i)

Thanks to this I can do proper OO now without having to delete stuff myself and without boost :)


Heed SiCranes warning.

It would be preferable to implement your class using composition. Your current code risks leaking memory for pvectors, precisely that which the class was designed to overcome.

Also note that your class doesnt allow you to specify a custom allocator for the vector.

Share this post


Link to post
Share on other sites
Oh...

I was actually first planning to make a new class with an std::vector that's a member of it. However, I'd have to type over ALL functions and operators of std::vector for it, because I want them all to be part of this class as well. So that's why someone suggested me on an irc channel to inherit from std::vector instead... I guess he didn't know that it was dangerous either.

But if the only problem could be polymorphism: I'm not planning to polymorph vectors.

EDIT:

I tested it with valgrind and it gave 0 errors, 0 leaks, for this code:

#include "pvector.h"
#include <iostream>

class A
{
public:

virtual int a() { return 1; }
virtual ~A(){}
};

class B : public A
{
public:

int a() { return 2; }
};

int main()
{
pvector<A*> v;

v.push_back(new A());

v.push_back(new B());

std::cout << v[0]->a() << "\n" << v[1]->a() << "\n";

return 0;
}


valgrind output:

1
2
==6021==
==6021== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 20 from 1)
==6021== malloc/free: in use at exit: 0 bytes in 0 blocks.
==6021== malloc/free: 5 allocs, 5 frees, 22 bytes allocated.
==6021== For counts of detected errors, rerun with: -v
==6021== All heap blocks were freed -- no leaks are possible.

Share this post


Link to post
Share on other sites
Privately inheriting from std::vector is safe, and you can expose the functions you want with the using keyword.


To view why your current code in unsafe, try this instead:

#include "pvector.h"
#include <iostream>

class A
{
public:

virtual int a() { return 1; }
virtual ~A(){}
};

class B : public A
{
public:

int a() { return 2; }
};

int main()
{
std::vector<A*>* v = new pvector<A*>();

v->push_back(new A());

v->push_back(new B());

std::cout << (*v)[0]->a() << "\n" << (*v)[1]->a() << "\n";

delete v;

return 0;
}



So, while your correct that non-polymorphic use of your class is safe, your class will compile without any errors if it is used unsafely.

Share this post


Link to post
Share on other sites
Quote:
Original post by Nitage
Privately inheriting from std::vector is safe, and you can expose the functions you want with the using keyword.


To view why your current code in unsafe, try this instead:
*** Source Snippet Removed ***

So, while your correct that non-polymorphic use of your class is safe, your class will compile without any errors if it is used unsafely.


But the thing is, it's not supposed to be used polymorphically!

I now added a comment like in the header saying that one should only use it non-polymorphically, and it should only contain pointers created with new.

So... if it'll only be used non-polymorphically, can it then be considered ok, or is it still considered very bad by programmers because it has the ability to be used in a bad way?

Also, can you please explain a bit more about the private inheriting and the using keyword? I've never seen it and have no idea where this using keyword has to be typed and how it works.

Thanks.

Share this post


Link to post
Share on other sites
Here's an example:

class Base
{
protected:
void Func1(){cout << "Func1" << endl;}
void Func2(){cout << "Func2" << endl;}
};

class Foo : Base
{
public:
using Base::Func1;
};



Foo foo;

foo.Func1(); //okay
foo.Func2(); //can't do that


Share this post


Link to post
Share on other sites
Quote:
Original post by Lode
I now added a comment like in the header saying that one should only use it non-polymorphically


You should rather be enforcing such usage (at compile time, if possible).

Not necessarily what you want, but here's a dirty hack to prevent it from being used "uncorrectly". (as a side note: it compiles fine on vs7.1)

template<typename T>
class pvector : public std::vector<T>
{
public:
~pvector() { for(size_t i=0; i<size(); i++) delete (*this); }
private:
void* operator new(size_t size);
void* operator new[](size_t size);
};



Another thing:
Watch that you won't be able to use templated functions specialized for std::vector, like std::swap. Instead, pvector will be delegated to non-specialized version (and in case of swap, this would crash the program).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!