data begins as some random value (not nullptr), pointing at some random memory. Then, you call delete on it. This is bad. You should only call delete on something that was allocated using new. Calling delete on random memory causes crashes.
Your constructor absolutely should not be calling delete on data. That makes no sense at all.What memory are you expecting to have been allocated that needs delete at that point?
Why are you trying to delete in your constructor?
Am I doing something wrong?
Yes you are trying to delete a pointer that hasn't had memory allocated with new. By default that pointer will be an uninitialized pointer that points to some random memory location (uninitialized). An uninitialized pointer will not be equal to a nullptr because a pointer must be explicitly initialized to a nullptr.
Well that's understandable, but then what about trying to reset the value of an object to default? How do I assign the default value without leaking previously-allocated memory?
This question kind of makes sense, but kind of doesn't.
1 2 3
Which of these three objects do you think should be given a "default value"? These are all simple primitive types. None of them really have any kind of meaningful "default" value. Zero is often used as a default value for an int, and nullptr is often used as a default value for a pointer, but those values don't have an inherent "default" meaning.
Well that's understandable, but then what about trying to reset the value of an object to default?
You tell me which of those you want to set to a "default value", and you tell me what that "default value" is, and I'll tell you how to set it to that value.
How do I assign the default value without leaking previously-allocated memory?
You are in a constructor, default constructor, of an object. That object has just been created. Nobody has had any chance to do anything with it before this moment. Nobody has had a chance to make that member pointer to point to any memory, allocated or not.
In other words there cannot possibly be any previously allocated memory.
Copy assignment is a different case. There you have an existing object with presumably some dynamically allocated memory. There you have to deal with it.
I do question your "default" too. Your default is to create one object of type T and call size 0. This differs from std::vector, which by default creates no objects of type T even if it would reserve space for some.
Which of these three objects do you think should be given a "default value"?
Let's say I have an AList<int> arr that I've initialized. Let's then say we get to arr = AList<int>(), as in I'm calling another constructor on arr. Would I need to handle memory re-allocation in the constructor, would that be left to the destructor, or would something else need to be done to de-allocate the previously-used memory?
So here you have an object of type AList<int>, named arr, created on line 1.
Then, on line 2, you create another object of type AList<int> (on the right hand side), and you use the copy assignment operator= , in which you presumably want to overwrite the values in arr with the new values from the temporary new object you created on the right hand side of line 2.
You would have an operator=, something of this form:
1 2 3 4 5 6 7 8 9 10 11 12 13
AList<T> & operator= ( AList<T> other )
delete data; // drop old array
size = other.size;
space = other.space;
data = new T[size];
for (int i = 0 ; i < size; i++)
data[i] = other.data[i];
Now the one on the left of line 2 is a copy of the temporary on the right, the memory that was being looked after in arr has been deleted safely, a new array created and copied.