3

I have template class Array where the template type T should be either pointer or not-pointer type.

template<class T>
class TArray
{   
     static const int max_len=100;
     T* data;
     int length;

  public:
     TArray(){data=new T[max_len]; this->length=0;} 
     void Add(T value){data[length++]=value;}   
     ~TArray();
};

The problem is how to free space, since we can not call delete for not pointer types like this

template<class T>
TArray<T>::~TArray()
{
    //for(int i =0; i < length; i++)
    //  delete data[i]; // NOT WORKING WITH OBJECTS THAT ARE NOT POINTERS !!!
    delete[] data;
}

Let's have addition class

class A
{
    int* a;
  public:
    A(int n){a = new int[n];}
    ~A(){delete[] a;}
};

and make two instances of template class

// Create array of doubles
TArray<double>* double_array = new TArray<double>();
delete double_array;

// Create array of pointers to class A
TArray<A*>* A_array = new TArray<A*>();
A* a = new A(5);
A_array->Add(a);
delete A_array;

When I call destructor for TArray<A*> I need to call destructor for class A but I don't know how, since the commented code in destructor is not compile (C2541) if we make for example array of doubles.

Dejan
  • 966
  • 1
  • 8
  • 26
  • 2
    Why are you `new`ing everything in sight? There's almost always some design flaw with code that has to use `new` so liberally. – Praetorian Oct 22 '12 at 15:13
  • What if the user of your class wants to create an array of pointers to data that wasn't allocated with `new`, such as string literals? They're out of luck, is what happens. For this reason, you should probably design your class *not* to call delete on each element in its destructor. If a user wants that behavior then they can get it by creating a `TArray`, with a type `T` that holds a pointer and deletes it in its destructor. In fact your class `A` does that. – Steve Jessop Oct 22 '12 at 15:33
  • 1
    @Praetorian: I think you could even say that there's *certainly* a design flaw in code that allocates a small type like `TArray` using `new` and then deletes it in the same scope. Maybe a Java programmer who hasn't yet realised that in C++ you can have a variable of class type (i.e. not a pointer and not a built-in arithmetic type)? – Steve Jessop Oct 22 '12 at 15:35
  • @Steve Jessop Thank you for useful comment that data may not be allocated with new and it will produce problem if I try to delete every element. I was C# programmer, so that is probably reason why I am `new`ing everything. However, I don't see problem with this, I use and pass pointer everywhere and avoid copy constructors. It seems to me that there is no solution for this except making two class for pointer and not pointer types. – Dejan Oct 22 '12 at 16:52
  • @Petar: you probably need to disassociate in your mind the idea of a pointer, the idea of how the memory to which that pointer points was allocated, and the idea of ownership of the memory referred to by the pointer. They are different things. Even if you did write a different class for pointer and non-pointer types, you still can't `delete[]` a pointer to a string literal. And you can pass pointers to local variables, provided the pointer doesn't outlive the variable -- you don't need to use `new` to avoid making copies. – Steve Jessop Oct 22 '12 at 17:07
  • @Steve Jessop I understand the difference and I know that making two class is not good general solution, but this is only I can do now to fix memory leaking I have in project (since this class is never used with template pointers created without `new`). In general, I don't see here any good solution which works in every case. – Dejan Oct 22 '12 at 17:24

3 Answers3

5

Inside the destructor, you can use std::is_pointer and only delete[] the data then.

The preferred alternative is to not manage memory yourself though (use std::vector or smart pointers).

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
3

You can develop two versions for your template. First, write the normal version template<class T>. Then, write a second pointer-only version that specializes your template by declaring it like this:

template<class T>
class TArray<T*>
alestanis
  • 21,519
  • 4
  • 48
  • 67
  • I implement this solution. Derive these two classes from abstract class because thay contain same code, and the derivated classes have only different destructor. – Dejan Oct 22 '12 at 17:06
  • I'm glad I helped :) don't forget to accept the answer you chose – alestanis Oct 22 '12 at 17:08
2

If you replace the raw pointers with std::unique_ptr or std::shared_ptr, the problem vanishes.

(If that's not what you want, then think twice about whether TArray should be managing this at all. Who will guarantee that the pointers stored in a TArray<T*> have been allocated with new? There's no RAII solution to that problem.)

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 2
    Why `shared_ptr`? I'd say `unique_ptr` should always be your first preference, unless you actually want to share the managed objects. – Praetorian Oct 22 '12 at 15:15
  • 1
    @Praetorian: `shared_ptr` does have one potential advantage over `unique_ptr` for use in containers, which is that the type of the deleter function is part of the type of `unique_ptr`, but not of `shared_ptr`. So if you want a container of pointers with wildly different deleters that might be a good enough reason to use `shared_ptr` even if the objects aren't shared. Not for this simple case, of course, where the questioner wants the default deleter. – Steve Jessop Oct 22 '12 at 17:11