Data Destruction In C++ - c++

So, for class I'm (constantly re-inventing the wheel) writing a bunch of standard data structures, like Linked Lists and Maps. I've got everything working fine, sort of. Insertion and removal of data works like a charm.
But then main ends, my list is deleted, it calls it's dtor and attempts to delete all data inside of it. For some reason, this results in a double free event.
All data is inserted into the list by these methods:
/*
Adds the specified data to the back of the list.
*/
template<typename T, class COMPFUNCTOR>
void List<T, COMPFUNCTOR>::append(T* d)
{
if(tail != NULL)
{//If not an empty list, simply alter the tail.
tail->setNext(new ListNode<T>(d));
tail = tail->getNext();
}
else
{//If an empty list, alter both tail and head.
head = tail = new ListNode<T>(d);
}
size++;
};
/*
Adds a copy of the specified data to the back of the list.
*/
template<typename T, class COMPFUNCTOR>
void List<T, COMPFUNCTOR>::append(const T& d)
{
this->append(new T(d));
};
The first method assumes that it owns the data passed into it; the second copies data passed into it. Now, for main:
int main(int argc, char** argv)
{
parser = new Arguments(argc, argv); //Uses a map<char, list<string>>; no direct bugs, insertion works fine.
if(parser->flagSet('f'))
{
printf("%s\n", parser->getArg('f').getFirst().str.c_str());
}
return 0;
}
This results in a stack dump, for a double free event.
The list destructor is defined as follows:
/*
Destroys the List and all data inside it.
*/
template<typename T, class COMPFUNCTOR>
List<T, COMPFUNCTOR>::~List()
{
while(head != NULL)
{
ListNode<T>* tmp = head; //Set up for iteration.
head = head->getNext();
if(tmp->getData() != NULL) //Delete this node's data then the node itself.
delete tmp->getData();
delete tmp;
}
};
If I comment out either the list destructor or the code in main's if statement, the program runs fine. Now, I'm not sure where this double delete is coming from.
List is destroyed on the end of main, which results in it deleting the data inside of it; which is either owned or copied into it, and only copies ever come out of it (the only time list passes out pointers of it's data is when you remove it from the list).
Obviously, something is created on the stack in main, when
parser->getArg('f').getFirst(); is called.
I read this as,
(de-ref pointer to parser)->(get a reference of the linked list).(acquire a copy of the first element in list [an std::string]);
Deleting a pointer to parser is no big deal (In fact, I should probably delete that, oops); deleting a reference shouldn't be a big deal either (just a candied up pointer); and deleting a copy of the first element should be a non-issue. Where have I gone wrong?
EDIT
The code for ListNode is as follows:
/*
Create a ListNode with the specified neighbor.
*/
template<typename T>
ListNode<T>::ListNode(T* d, ListNode<T>::ListNode* neighbor)
{
data = d;
next = neighbor;
}
/*
Deletes the ListNode.
*/
template<typename T>
ListNode<T>::~ListNode()
{
next = NULL;
if(data != NULL)
delete data;
data = NULL;
}
ListNodes only ever take pointers to their data, they only ever delete their data when they die with non-null data pointers. The List itself also only ever deletes stuff if it is non-null. All deleted data is set to NULL.
Oh, and the data right now is std::string, I have no control over it's copy constructor, but I would assume it's properly implemented.

Does the ListNode destructor also delete its data?
Edit: ah, now that you've posted your source - note that delete does not set to null... so your null test will still yield false.

Looking at your ListNode class it is obvious that there is an ownership mismatch. It is a good rule of thumb for design that not only should every allocation be matched by a matching de-allocation but that these should be performed by at the same layer in the code or ideally by the same object. The same applies to any resource whose acquisition needs to be paired with a release.
It's obvious that this guideline isn't being followed here and that is a root of a lot of your issues.
template<typename T>
ListNode<T>::ListNode(T* d, ListNode<T>::ListNode* neighbor)
{
data = d;
next = neighbor;
}
template<typename T>
ListNode<T>::~ListNode()
{
next = NULL;
if(data != NULL)
delete data;
data = NULL;
}
ListNode deletes something that it didn't allocate. While you allow for the possibility of a null data member, you are going to make things simpler if you don't allow this. If you want a list of optional items you can always use you're template with a smart pointer type or a boost::optional.
If you do this, and then make sure that your ListNode class always allocates and deallocates the copy of the item, you can then make the data member a T instead of a T*. This means that you can make your class something like this:
template<typename T>
class ListNode
{
public:
explicit ListNode( const T& d )
: data(d), next()
{
}
T& getData() { return data; }
const T& getData() const { return data; }
ListNode* getNext() const { return next; }
void setNext(ListNode* p) { next = p; }
private:
ListNode* next;
T data;
}
I've moved to using references for things which can't now be null. Also, everything that's owned is now a data member and things that that class doesn own (next) are referred to by pointer.
Of these two functions, I hope that the second was a private function because your destructor always assumes that the List (via a ListNode) owns the data pointer so having a public function which takes a raw pointer that doesn't take a copy is potentially dangerous.
template<typename T, class COMPFUNCTOR>
void List<T, COMPFUNCTOR>::append(T* d);
template<typename T, class COMPFUNCTOR>
void List<T, COMPFUNCTOR>::append(const T& d);
With the change to ListNode above we can implement the second of these without needing the help of the first quite simply.
template<typename T, class COMPFUNCTOR?
void List<T, COMPFUNCTOR>::append(const T& d)
{
ListNode<T>* newNode = new ListNode<T>(d);
if (!tail)
tail->setNext( newNode );
else
head = newNode;
tail = newNode;
size++;
}
The destructor 'walk' for the List remains similar to what you have except there should be no attempt to manually delete the owned data of the ListNode, that happens automatically when you delete the ListNode itself.
Note: all my code is untested, it's for exposition only!

You haven't posted the code for it, but my guess would be that ListNode's destructor is deleting its data, after you've already deleted it in List's destructor.

It's not the source of your problem, but you don't have to check if something is NULL or not before you delete it. Calling delete on NULL is a no-op. I don't think you've made this error, but I've seen people write
if(data != NULL)
delete data;
and think that the behavior is
if(data is properly allocated)
delete data;
which is, of course, not true.

Did you honor the rule of three? Double deletion often happens when an object that is supposed to "manage a pointee" is copied. Either make your nodes noncopiable and nonassignable or define your own copy ctor and assignment operator "properly".

Check your copy ctor. If an argument is being copied by value, it would have to use the no-arg ctor and that call would be inserted by the compiler so you wouldn't know it was happening.
Try putting log statements in the copy contructor to see if it's being called when it shouldn't be.

Related

Invalid Adress Specified to RtlvalidateHeap in Destructor for doubly linked list

I keep getting this error when I run my program.
HEAP[PA1.exe]: Invalid address specified to RtlValidateHeap( 00C40000, 00C48880 )
PA1.exe has triggered a breakpoint.
I narrowed it down to my destructor but, im not sure why its causing it.
List::~List()
{
if (!nullptr) {
Item* toDelete = Head;
while (toDelete != NULL) {
Item* next = toDelete->Next;
delete toDelete;
toDelete = next;
}
}
Edit: I see that my problem is in my constructors but, im not sure what i need to change. The error happens when i begin to do a deep copy. So my guess is that its the copy constructor.
List::List()
{
Head = 0;
Prev = 0;
Next = 0;
}
List::~List()
{
Item* move = Head;
while (Head)
{
Head = move->Next;
delete move;
move = Head;
}
}
List::List(const List& copy)
{
Head = copy.Head;
Next= copy.Next;
Prev= copy.Prev;
}
List& List::operator=(const List& t)
{
Head = t.Head;
Prev = t.Prev;
Next = t.Next;
return *this;
}
While your code is not sufficient to conclude for sure, looking at the symptoms and your destructor, it is highly probable that you did not respect the rule of 3.
The rule of 3 tells you that if you need a specific destructor, for example because you have a pointer, you need to implement at least the copy constructor and the copy assignment.
Why ?
You have a pointer Next in your class.
Somehow you allocate some memory and put the address in that pointer (because if you wouldn't , you wouldn't delete the pointed object at the end
If you do not have a copy construction and a copy assignment, when ever you cpy a List element, you will copy its pointer as it is. The first object to be deleted (e.g. a temporary object?) would delete the pointer. And when the second object get destroyed, its destructor will delete a pointer that is already deleted.
Now if you do not like to take care of the rule of 3, there is a better alternative: avoid using raw pointers. Try to use shared_ptr instead. These pointer manage memory by themselves (e.g. deleting the object when it's no longer used). Including in case of copying and deleting the copy.

Copy Constructor for a Linked List Stack Class: mine is copying backwards

So I've combed through what is already on this site about linked list stacks. Mine is a template class and I've also created a Node structure. From what I've seen with other questions, I understand that this isn't always the best considering standard library implementations already existing in C++ as mentioned in this first answer BUT this is for an assignment so it's all in the name of science.
This is the structure so you have an idea of what I've named things
template <class T>
struct Node
{ public:
//data members
T data;
Node<T> *next;
//methods
Node(const T &);
};
template <class T>
Node<T>::Node(const T &newData)
{
data = newData;
next = NULL;
}
Nothing fancy.
These are my Stack class's data members for reference
private:
Node<T> *stkTop; //or headPtr, so to speak
int stkSize;
And this is my copy constructor as is
template <class T>
Stack<T>::Stack(const Stack<T> &existStack)
{
Node<T> *currentNode = existStack.stkTop;
stkSize = 0;
stkTop = NULL;
while( currentNode != NULL )
{
Node<T> *ptr = new Node<T>(0);
ptr -> data = (currentNode -> data);
ptr -> next = stkTop;
stkTop = ptr;
currentNode = currentNode -> next;
stkSize++;
}
So far so good...
I'm even obeying the Rule of Three. I also have an overloaded assignment operator and a destructor (though to be honest the assignment operator is the same exact code, I've just included a test for self assignment).
I mean, it KIND OF works. All the values show up...just in reverse order... Totally stumped.
The implementing program I have just runs a couple short tests like explicitly calling the constructor, assigning a stack to another stack, assigning a stack to itself. Everything looks fine except for the order. I'm assuming I'm traversing my list wrong or something when copying values over....I don't know :(
You are going down (stack top to stack bottom) existStack with currentNode while you are going up this stack. The first item you inserted should have been the top, but instead the last node you inserted is the top.

operator= overload for Linked List issues

I'm having issues with my implementation of the overloaded = operator for a linked list. The List class contains a Node* head pointer and a struct Node containing T* data and Node* next, where T is a template typename. I'm having trouble with what is happening at the end of the operator function where the destructor (in this case handled by makeEmpty) is being called twice by the end of the operator, once after iterating through the list and creating a new list of the same nodes and once after the operator function exits.
Here is the makeEmpty implementation:
// Does the work of the Destructor
template <typename T>
void List<T>::makeEmpty() {
cout << endl << endl << "DESTRUCTOR CALLED" << endl << endl;
List<T>::Node* tempPtr = head;
if (head != NULL) {
List<T>::Node* nextPtr = head->next;
for(;;) {
if (tempPtr != NULL) {
delete tempPtr->data;
tempPtr = nextPtr;
if (nextPtr != NULL)
nextPtr = nextPtr->next;
/*tempPtr = head->next;
delete head;
head = tempPtr;*/
}
else break;
}
}
}
Here is the operator= overload implementation:
// Overloaded to be able to assign one list to another
template <typename T>
List<T> List<T>::operator=(const List& listToCopy) {
List<T> listToReturn;
listToReturn.head = NULL;
List<T>::Node* copyPtr = listToCopy.head;
List<T>::Node* thisPtr = head;
if (copyPtr != NULL && thisPtr != NULL) {
for(;;) {
if (copyPtr != NULL) {
T* toInsert = new T(*copyPtr->data);
listToReturn.insert(toInsert);
copyPtr = copyPtr->next;
}
else{cout << endl << listToReturn << endl << endl; return listToReturn;}
}
}
// if right-hand list is NULL, return an empty list
return listToReturn;
}
I've been debugging for a little bit and it seems that the second time the destructor is called, the head of the list being destroyed contains unreadable memory. I'm not even sure why the destructor is being called twice within the operator since the only list that needs to be destroyed is the listToReturn after it has been returned. (maybe my logic is flawed somewhere... I've just been thinking about this for too long)
If you guys need anymore info about the code, I'd be glad to provide. As usual, this is for an assignment, so I'm only asking for tips that may help me get in the right direction. So thanks to everyone for looking and helping!
You asked for pointers and tips, so I'm giving it:
1) Unnecessary dynamic data member
Your List<T>::Node does not need a dynamic member for the underlying data value. It should be constructible from a const T& and if implementing a C++11-compliant move-construction idiom, a T&& as well. And both should initialize the next member to nullptr
2) Copy-constructor for List<T> is mandetory
In accordance with the Rules of Three, Four, or Five, your class has dynamic members, and as such must properly manage them in a copy-construction and assignment operator action (or hide said implementations, but clearly that isn't an option for you, as it is part of your assignment).
3) Utilize the class copy-constructor for the assignment operator overload
Overloading an assignment operator where dynamic allocations are involved (and they are here, as your linked list of Node mandates it) should ideally utilize the copy-constructor and the copy/swap idiom for lifetime management. This has a number of benefits, the two most important being potentially eliding the copy-operation by an optimizing compiler, and exception safety that maintains objects in their original state.
4) The List<T>::operator = override should return a reference to the current object
The current object being assigned-to (the left side of the operator) should be a by-reference return result. It should be the object that is modified. This is part and parcel with the standard implementation for such an operator. You return a copy, but the original object remains untouched, thereby utterly defeating the purpose of the assignment operator (i.e. the lvalue side isn't actually modified in your implementation at all)
Each of these is addressed in detail below:
Unnecessary dynamic data member
As it is not posted, I must be somewhat soothsaying in what List looks like. I imagine something like this:
template<class T>
class List
{
private:
struct Node
{
T* data;
Node* next;
};
Node *head;
// other members and decls...
};
With this your insertion and copy operations are having to take significant advantages to manage dynamic allocations of T objects where they shouldn't need to. List<T> should certainly own a chain of Node; but Node should own the actual T object, and be responsible for managing it therein; not List<T>. Consider this instead:
template<class T>
class List
{
private:
struct Node
{
T data;
Node* next;
Node(const T& arg)
: data(arg), next()
{}
Node(const Node& arg)
: data(arg.data), next()
{}
private:
// should never be called, and therefore hidden. A
// C++11 compliant toolchain can use the `delete` declarator.
Node& operator =(const Node&);
};
Node *head;
// other members and decls...
};
Now when a new node is needed to hold a T object (such as in an insertion operation) the following can be done:
template<typename T>
void List<T>::someFunction(const T& obj)
{
Node *p = new Node(obj);
// ...use p somewhere...
}
Copy-constructor for List<T> is mandetory
Your linked list, by its very nature, manages a dynamic chain. Therefore this class must implement copy-construction and assignment operations, the latter being part of your assignment and the reason you posted your question. Duplicating a linked list is fairly straight-forward, but some, for whatever reason, make it harder than it seems. The following is one of my preferred ways of doing it:
template<typename T>
List<T>::List(const List<T>& arg)
: head()
{
Node **dst = &head;
const Node* src = arg.head;
while (src)
{
*dst = new Node(*src); // invoke Node copy-construction
dst = &(*dst)->next; // move target to new node's next pointer
src = src->next; // advance source
}
}
This uses a simple technique of a pointer-to-pointer to hold the address of the pointer being populated with the next new node. Initially it holds the address of our head pointer. With each new node added it is advanced to hold the address of the newly added node's next member. Since Node(const Node&) already sets next to nullptr (see prior section) our list is always terminated properly.
Utilize the class copy-constructor for the assignment operator overload
The List<T>::operator = override should return a reference to the current object
Once we have a solid copy constructor, we can use it for overriding our assignment operator. This is done in a not-so-apparent way, but I'll explain after the code:
template<typename T>
List<T>& List<T>::operator=(List<T> byval)
{
std::swap(head, byval.head); // we get his list; he gets ours
return *this;
}
I'm sure you're looking at this and thinking, "Huh??". It deserves some explanation. Look carefully at the parameter byval being passed in, and consider why I named it as I did. It is not a traditional const reference you're likely used to seeing. It is value copy of the right-hand-side of the assignment expression. Thus, to create it the compiler will generate a new List<T>, invoking the copy-constructor to do so. The result of that copy is the temporary object byval we have as our parameter. All we do is exchange head pointers. Think about what that does. By exchanging head pointers we take his list and he takes ours. But his was a copy of the original right-side of the assignment expression, and ours, well, we want it deleted. And that is exactly what will happen when the destructor for byval is fired once this function is finished.
In short it makes code like this:
List<int> lst1, lst2;
lst1.insert(1);
lst2.insert(2);
lst1 = lst2; // <== this line
execute our function on the marked line. That function will make a copy of lst2, passing it into the assignment operator where lst1 will exchange head pointers with the temporary copy. The result will be lst1's old node will be cleaned up by the destructor of byval, and the new node list is properly in place.
There are a number of reasons to do this. First, it makes your assignment operator exception-safe. If an exception is tossed (usually a memory allocation exception, but it doesn't matter) no memory is leaked, and the original object lst1 remains in its original state. Second, the compiler can elide this away entirely if it chooses and the conditions are correct.
Anyway, these are some ideas and some points of bugs in your implementation. I hope you find them useful.

Is this a deep copy and properly implemented = operator for a C++ Linked List?

I'm running into some bugs now that I'm trying to use the doubly linked list class that I made. My implementation of the = operator looks like the following:
template <typename T>
Dlist<T>& Dlist<T>::operator=(const Dlist &l)
{
copyAll(l);
return *this;
}
template <typename T>
void Dlist<T>::copyAll(const Dlist &l)
{
node *copyList = new node;
copyList = l.first;
while(copyList){
insertFront(copyList.first->o);
copyList = copyList->next;
}
delete copyList;
}
Note that o is a pointer for the data in a node in the List.
My intent is for copyAll to be a true deep copy. Is this not the case? Is there something wrong with my class method definitions here? I'm new to linked lists so help is much appreciated!
EDIT: specifically the issue I'm having is when I make a List and fill it, then make a new list and set it equal to the first, anytime I do something to the second list, it also alters the first list.
EDIT2: Here's the class itself. I'm not allowed to add any other member functions:
template <typename T>
class Dlist {
public:
// Operational methods
bool isEmpty();
// EFFECTS: returns true if list is empty, false otherwise
void insertFront(T *o);
// MODIFIES this
// EFFECTS inserts o at the front of the list
void insertBack(T *o);
// MODIFIES this
// EFFECTS inserts o at the back of the list
T *removeFront();
// MODIFIES this
// EFFECTS removes and returns first object from non-empty list
// throws an instance of emptyList if empty
T *removeBack();
// MODIFIES this
// EFFECTS removes and returns last object from non-empty list
// throws an instance of emptyList if empty
// Maintenance methods
Dlist(); // ctor
Dlist(const Dlist &l); // copy ctor
Dlist &operator=(const Dlist &l); // assignment
~Dlist(); // dtor
private:
// A private type
struct node {
node *next;
node *prev;
T *o;
};
node *first; // The pointer to the 1st node (NULL if none)
node *last; // The pointer to the 2nd node (NULL if none)
void makeEmpty();
// EFFECT: called by constructors/operator= to establish empty
// list invariant
void removeAll();
// EFFECT: called by destructor/operator= to remove and destroy
// all list elements
void copyAll(const Dlist &l);
// EFFECT: called by copy constructor/operator= to copy elements
// from a source instance l to this instance
};
Basically the difference between a shallow copy and a deep copy is whether you copy just the pointer itself or copy all the data the pointer points to. Don't forget to also invoke the base class and copy all of its members as well to avoid partial initialization if your object is derived!
Generally you want to provide a copy constructor for this purpose. The assignment operator is similar to copying but assignment is actually done on an already constructed and probably already initialized object.
As to whether you are doing it right well that depends on the implementation details of your class most of which you have not shown here. What you have shown looks incomplete.
You may want to consider using std::list until you are more familiar with C++ and data structures before attempting to implement your own version as well. It's rare to really need to reinvent the wheel nowadays other than for curiosity sake or more complete learning of underlying concepts.
You say "some bugs". It's helpful to mention what those are when asking a question.
That said, look at the first two lines of copyAll. The first line allocates a new node, the second overwrites that pointer, losing it forever. Maybe you meant copyList.first = l.first. You'll also need to construct new nodes inside the while loop.

Destructor deletes copy in function returning dynamic structure

Ok everyone, noob question.
So I have a template class implementing a singly linked list. A function in a class in my program returns one of these lists.
psList<int> psObj::getList() const {
return List;
}
So what is happening is on the call to return List the copy constructor kicks in which does its job nicely and creates a copy of the list. However then the function finishes and goes out of scope and calls the Destructor! All of a sudden the returned linked list gets deleted, as this is what my destructor does, deletes a list and deletes it well.
I understand I could just make the return type a pointer to the head of the copied list and all would be well and good, but the trouble is I would still not be able to create a function returning a copy of a dynamic structure, even if I wanted to, and I do want to.
I was asked for more code.
Here is the copy constructor, which obviously does a deep copy
template<class psClass>
psList<psClass>::psList(const psList &original) {
head = NULL;
if(original.head != NULL) {
psNode<psClass>* iterator = original.head;
while(iterator != NULL) {
pushback(iterator->data);
iterator = iterator->next;
}
}
}
Here is the destructor
template<class psClass>
psList<psClass>::~psList() {
erase();
}
Here is the erase function the destructor calls.
template<class psClass>
void psList<psClass>::erase() {
psNode<psClass>* iterator = head;
psNode<psClass>* buff;
while(iterator != NULL) {
buff = iterator->next;
delete iterator;
iterator = buff;
}
}
So yes I am doing deep copies and deep destructs. The problem is not the depth. The problem is thus. In the original function a deep copy is made and returned. The function goes out of scope and the deep destructor is called on the copy. No more copy.
To better explain here is what it looks like in the debugger
The original list before the getlist function call.
head 0x616080
data 2
next 0x616060
data 12
next 0x0
Here is the List of "return List" once inside the getList function
head 0x616080
data 2
next 0x616060
data 12
next 0x0
Same thing.
Here are the lists "original" and "this" at the end of the copy constructor.
"this"
head 0x63c900
data 2
next 0x63a940
data 12
next 0x0
"original"
head 0x616080
data 2
next 0x616060
data 12
next 0x0
Everything looks great doesn't it.
Now we are back in the getList function and about to step into the final bracket.
psList<int> psObj::getList() const {
return List;
} // This bracket
The list List back in this function is what you would expect it to be
head 0x616080
data 2
next 0x616060
data 12
next 0x0
And now that we step into the final bracket the destructor is called where there is
/*
* No idea what the in chrg thing is or why the debugger is telling me it was
* optimized out but I mentioned it here cause maybe it has something to do with my
* problem
*/
this 0x7ffffffe650
__in_chrg value optimized out
// Look familiar? well it should cause it is the head of the list I returned.
head 0x63c900
data 2
next 0x63a940
data 12
next 0x0
Then bam! The list I just copied and returned gets deleted by the destructor cause it goes out of scope.
To reiterate my original question after that detour. How do I get a dynamic structure to be returned by a function using a deep copy, without having the destructor destroy the said copy.
More code on request
// Simple single link node with default constructor initializing the link to NULL.
template <class psClass>
struct psNode {
psClass data;
psNode<psClass>* next;
psNode() {
next = NULL;
}
};
and the push back function
template<class psClass>
void psList<psClass>::pushback(psClass object) {
psNode<psClass>* ptr = new psNode<psClass>;
ptr->data = object;
if(head == NULL)
head = ptr;
else {
//Have to find the tail now
psNode<psClass>* tail;
psNode<psClass>* iterator = head;
while(iterator != NULL) {
tail = iterator;
iterator = iterator->next;
}
tail->next = ptr;
}
}
And yes I know keeping track of tail would be easier.
Here is the psList class definition:
template <class psClass>
class psList {
public:
psList();
~psList();
psList(const psList &original);
psList(psNode<psClass>* _head);
void erase();
void pushfront(psClass object);
void pushback(psClass object);
bool isEmpty() const;
psNode<psClass>* front() const;
private:
psNode<psClass>* head;
};
No overloaded assignment operator yet. I plan to add it in after I jump over this hurdle.
It would appear that psList's copy constructor makes a shallow copy instead of a deep one. In general, if you manage resources in a class, then you need non-trivial copy constructor, assignment operator and destructor (the "big three"). Please show us the code of psList.
What you are currently doing is effectively as follows:
psList<int> psObj::getList() const { return psList<int>(List); }
This creates a copy of the member List and copies it across to the frame where getList was called. The way that it is copied across depends upon how you call it. If you construct a new psList object from this data, as in
psList<int> newList = obj.getList(); // same as psList<int> newList(obj.getList());
the copy constructor is used. Often the two copies are reduced to a single copy by RVO.
Alternatively, if you are copying to an existing object, as in
psList<int> newList;
newList = obj.getList();
the original object's state is replaced with the data from the returned result, via the assignment operator. If you don't declare one of your own, the compiler will define a public assingment operator for you. But this is simply going to be a copy of each member of your object. That is:
psList & psList::operator=(const psList& src) {
head = src.head;
}
so in the calling code what happens is as follows:
psList<int> newList; // psList default constructor called
newList = obj.getList(); // 1) obj.List copied via copy constructor within getList
// 2) copy of obj.List copy-assigned to newList (simple copy of head pointer)
// 3) copy of obj.List destructed
// newList now has head pointing to destroyed data
which in your case is not what you want, and what you should have done was made sure the copy assignment genuinely peformed the intended deep copy (see copy-and-swap for a method to do so via the copy constructor you've already implemented).
Hence the rule of three: if you need to define your own implementation of any one of the destructor, copy constructor and copy assignment, then you need to define them all (or at least declare the copy assignment and copy ctor private to render your class uncopyable).
As an aside, why not return a reference:
const psList<int> & psObj::getList() const { return List; }
and make the calling function decide whether to make a copy?
psList<int> localList(localPsObj.getList());