views:

469

answers:

6

Hello all,

For a program of mine I made a small function to clear the various std::vectors of pointers that I have.

template <class S>
void clearPtrVector(std::vector<S*> &a,int size)
{
    for(size_t i = 0; i < size; i++)
         delete a[i];

    a.clear();
}

I must have done something wrong here though since when calling this function in a destructor like so :

clearPtrVector(neurons,neurons.size());

I get the following undefined reference two times:

undefined reference to `void clearPtrVector<Neuron>(std::vector<Neuron*,std::allocator<Neuron*> >&, int)'

I have to admit I am not familiar with what the std::allocator is, so I can not guess what the problem might be here. Any help is really appreciated. Thanks in advance!

-Lefteris

+6  A: 

Hot Fix

Write following instead:

  template <class Vector>
  void clearPtrVector(Vector &a)
  {
    for(size_t i = 0; i < a.size(); i++)
         delete a[i];

    a.clear();
  }

Make sure you define the template somewhere where compiler can see it before each use of the template. If you do not create a declaration, you should be safe, as otherwise you should get a compile error. If you do create a declaration for any reason, be double carefull to include definition everywhere as needed.

Redesign

That said, I think the proper solution would be to rethink your design and to use a container which will handle the destruction properly, so that you do not have to do it manually, which is tedious, and almost impossible to do correctly if you need exception safety. Use std::shared_ptr instead of raw pointers, or std::auto_ptr with a container which is able to hold them (std::vector cannot store auto_ptr values). One possible solution would be to use Boost Pointer Container

Suma
Thanks Suma, for now I will use the hotfix. I will think of the auto_ptr solution but First I will have to read up on auto_ptrs. I have never ever used them before. Gotta see their pros and cons.
Lefteris
I would suggest to lookup the boost smart pointers because the standard does not allow a vector of auto_ptr's.
TimW
Never ever create a vector of auto_ptrs. Take a look at "Effective STL -Revised" by Scott Mayers for the reasoning behind it.
Naveen
This answer has wrong reasoning. Whether you write `std::vector<A>` or `std::vector< A, std::allocator<A> >` isn't important, since the allocator is put exactly like that as a default argument. The actual link error happened because the definition wasn't available in the header. The "Redesign" chapter also contains a wrong recommendation, as naveen explains. Please correct those issues.
Johannes Schaub - litb
it *is* important when you use it as a template-template parameter like here: template<template<typename> class V> void f(); // won't accept vector. vector has at least (not exactly! implementations could add optional parameters as they wish!) 2 parameters. In our case, however, we don't handle template template parameters
Johannes Schaub - litb
I have read others telling it must go into the header, and I agree based on the error message I would really think the same. However, if OP did not have it it the place where compiler can see it, it would not even compile (unless he would provide a template declaration only). Without seeing the real code I cannot decide what exactly the problem is. To stay safe, I will add this into the answer.As for auto_ptr not working with vector, this is already included in the answer.As for my two parameters advice, I am sorry for it and I am removing it, I can see now it is plain wrong.
Suma
Nice answer now. Don't worry about the two parameter advice you gave before. We all have our segfaults now and then :)
Johannes Schaub - litb
+3  A: 

Is your clearPtrVector implementation in a header file? Because if it's in a separate .cpp file, the linker will not find it.

Igor Krivokon
Raindog you were right, that's what was happening in the first place. I forgot that it is not a class-owned function and had its implementation in a cpp file. Corrected that now .... and made a note to never do that mistake again :) . Thanks
Lefteris
+2  A: 

Make sure you have this function in a header file (.h, *.hpp) because if you defined it in a source file with a prototype in a header file you'll got an undefined reference linker error.

Undefined reference error means that compiler has found the reference of the function but linker has failed to find an reference of that function amongst object files. Any template function has to be defined in a header file so the compiler will be able to put it in any source file which makes use of the function.

Serge
+3  A: 

A few things:

In your original code, don't pass in the size; just get it from the vector:

template <class S>
void clearPtrVector(std::vector<S*> &a)
{
    for(size_t i = 0; i < a.size(); ++i)
    {
         delete a[i];
    }

    a.clear();
}

Secondly, just pass in the vector itself, not the type it points to:

template <class Vector>
void clearPtrVector(Vector &vec)
{
    for(size_t i = 0; i < vec.size(); ++i)
    {
         delete vec[i];
    }

    vec.clear();
}

Thirdly, that error sounds like you have it placed in a .cpp file. The code will be generated when you first call the function, which means the compiler needs to know the definition of the function. Move the function into the header file, so the compiler can find it.

Lastly, consider using things more suited to this:

GMan
A: 

Isn't the redesign a duplicate of cleaning-up-an-stl-list-vector-of-pointers

Remark

If you don't use one of the smart pointers, use boost::checked_delete function instead of delete to make sure you are not deleting an incomplete type.

TimW
Not quite. This example has some additional bugs.
finnw
A: 

As I provided in my answer to your first question http://stackoverflow.com/questions/891913?sort=newest, one answer is as follows:

template <class C> void FreeClear( C & cntr ) {
    for ( typename C::iterator it = cntr.begin(); 
              it != cntr.end(); ++it ) {
        delete * it;
    }
    cntr.clear();
}

which works for all container types. Notice that the size parameter is NOT provided but is obrtained from the collection - this is the correct way to design such functions, providing the size as a separate value can only lead to disaster.

anon
Yeah I know you are right, I did it like that at first just out of bad habit. It is already corrected in my code. Thanks for taking the time to answer.What bothers me is so many people here telling me to use other kind of pointers which I don't even know how to use. Is it such a huge drawback in anyone's program to use normal pointers?
Lefteris
anon
You shouldn't be bothered by people suggesting improved ways to program :) Learning them will only make you a stronger programmer.
GMan
I see. I guess I will have to check all my options and then settle on something that is correct and effective but also fits my programming habits (the good ones :p )Thanks for the advice Neil.
Lefteris
Oh and GMan when I said bothered I meant ... not in the bad sense, just in the sense that makes you think over what you are currently doing :)
Lefteris