2

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.

  • You should familiarize yourself with [The Rule of Three](http://stackoverflow.com/questions/4172722/). Also, unless this is homework or a learning exercise, implementing your own linked list is definitely *not* recommended. – fredoverflow Nov 21 '10 at 15:05
  • The linked list that you return is supposed to be destroyed, surely, as it has been copied in the return statement and the original is local to the function which you return from? – CB Bailey Nov 21 '10 at 21:55
  • You should probably show how `psList`, `psNode` and `psList::pushback` are defined. – CB Bailey Nov 21 '10 at 21:56
  • That makes sense but the one local to the function and the one I returned are the _same list_ in memory. If it were not a dynamic structure then I would declare someVar, return someVar which creates a copy of itself and then someVar in the function will be deleted. But now what happens is I declare some list, I return some list, and since lists are addresses in memory I also delete the list. Maybe I am trying to do something impossible but surely you can return a dynamic structure without also destroying it somehow. I will put up some output from the debugger to better explain. –  Nov 21 '10 at 22:00
  • I thought you were doing a "deep" copy in your copy constructor? If so, they're not the same list in memory, the returned value is a copy of the one local to the function. – CB Bailey Nov 21 '10 at 22:07
  • @Avram, rather than putting up some debugger output, can you give more of your implementation? As Charles requested, `pushback` and the members of `psList` and `psNode` are needed to make complete sense of what you're doing. – beldaz Nov 21 '10 at 22:22
  • Hold on, what do you mean by "original list before the getlist function call"? What original list? Are you performing some self-assignment? Is your copy assignment operator self-assignment safe? Where's the code that calls `getList`? – CB Bailey Nov 21 '10 at 22:25
  • psList psObj::getList() const { return List; } belongs to the class psObj. psObj has a member of type psList and the getList function attempts to retrieve a copy of that list. By original list I mean the psList member of the psObj class. –  Nov 21 '10 at 22:32
  • I have added more code up there. I don't yet have an overloaded assignment operator but I don't think it is or would be called at all in what I am currently trying to do. I do plan on adding one once I get this sorted out. One problem at a time ;) –  Nov 21 '10 at 22:49
  • `pushback()` looks OK, so copy ctor seems sound. You do still need an assignment since your returned object is copied between frames unless the line that calls `getList` is constructing a new object (in which case the copy construct may even be optimised out). To echo Charles again, what is the code that calls `getList`? – beldaz Nov 21 '10 at 23:07
  • psList newList = obj.getList(); Yes the debugger is mentioning some kind of optimizing out. –  Nov 21 '10 at 23:15
  • You also have a `psObj`. What's that? – beldaz Nov 21 '10 at 23:34

2 Answers2

2

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.

dennycrane
  • 2,301
  • 18
  • 15
0

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());
beldaz
  • 4,299
  • 3
  • 43
  • 63
  • How did you reach this conclusion? The unknown part of the constructor (`pushback`) only receives the `data` part of the `psNode` so it would be difficult for it to reconstruct a pointer to the origin `psNode`. – CB Bailey Nov 21 '10 at 22:13
  • no the copy constructor most definitely makes a deep copy. pushback creates a pointer to a blank new node, the data is copied into this node, and then the last node in the already made list is linked to it (before it was linked to null). The link in the new node already points to null so nothing to be done there. –  Nov 21 '10 at 22:27
  • @Charles: Good point, missed that only data passed to `pushback`. Replaced my rubbish answer with something hopefully more useful. – beldaz Nov 21 '10 at 22:42
  • Forgive my ignorance, but why would I need to do something like psList psObj::getList() const { return psList(List); } To my understanding when a function needs to return a value, it calls the copy constructor. Isn't this just needlessly calling the copy constructor twice? –  Nov 21 '10 at 23:00
  • @Avram: Checking Strousrup TCPP I think there's no difference - there is an implicit copy in the value return line, I just made it explicit. – beldaz Nov 21 '10 at 23:18
  • The problem seems to be that originally I had psList newList; newList = obj.getList(); Which was calling an assignment operator no?. That operator then poceeded to exhibit the behavior I documented above. I seemed to have changed it to one line at some point while trying things and the problem went away. If someone could explain why the assignment operator when not overloaded calls a copy constructor and then a destructor on the copy that would be nice. But as it is I have everything working so no problems Thanks everyone for the help –  Nov 21 '10 at 23:34
  • @Avram: Thanks. Hope the edits I've added explain everything you were after. – beldaz Nov 22 '10 at 00:21