tags:

views:

114

answers:

3

I'm currently implementing a copy constructor for a Linked List class. When I create a new instance of the class with another Linked List as a parameter, the constructor is being called for the object I'm passing as the parameter. This is leaving me confused beyond belief. Here is the section necessary to understand what's going on in the main method:

int main()
{
   LinkedList ll;
   LinkedList ll2(ll);
}

So, instead of calling the copy constructor for ll2, the copy constructor for ll is being called. I've confirmed that the size of ll is correctly 3 before I attempt to copy ll into a new LinkedList, namely ll2. After the copy though, both have the same size, greater than 3, but even more weird, the copy constructor of ll is being called, not that of ll2. Since I'm using VC++, I've stepped through the program to confirm this.

Here is the copy constructor for the LinkedList class:

LinkedList::LinkedList(const LinkedList & other)        
{
   LLNode *otherCurNode = other.GetFirst();
   if (otherCurNode != NULL)
   {
      front = new LLNode(otherCurNode->GetValue(), NULL, NULL);
      back = front;
   }
   else
   {
      front = NULL;
      back = NULL;
   }
   LLNode *curNode = front;
   while (otherCurNode != NULL)
   {
      Insert(otherCurNode->GetValue(), curNode);
      curNode = curNode->GetNext();
      otherCurNode = otherCurNode->GetNext();
      back = curNode;
   }
   numNodes = other.GetSize();
}   

My apologies if this ends up being a simple problem - I'm fairly new to C++. Any help will be greatly appreciated!

+7  A: 
LinkedList ll = LinkedList();

This creates a linked list instance, and this instance is then copy constructed. This looks like a Java or C#-ism. It is actually equivalent to:

LinkedList ll(LinkedList());

To create an empty linked list, simply write:

LinkedList ll;

This will implicitly call the default constructor.

Also, make sure that you do have a default constructor which properly initializes the linked list to empty. If you don't have one then the list's variables will end up with whatever garbage values are on the stack.

John Kugelman
I guess that's my Java/VB/C# shining through =). I fixed my original code but the problem is still present.
AndyPerfect
No, do not add parentheses to call the constructor explicitly. For constructors that take no arguments, it will be interpreted as a function declaration.
jamesdlin
@jamesdlin Good point. I was about to add a note about the `typename` keyword but really it's better I just remove that sentence entirely.
John Kugelman
+2  A: 

Weird things are often a sign of improper memory handling. I can see one immediate problem with the code you posted and there may be similar problems in other functions.

On the line while (otherCurNode->GetNext() != NULL), bad things will happen if otherCurNode is already NULL. This is the case both when the other list is empty as well as when you reach the end of the list via otherCurNode = otherCurNode->GetNext();. You really want to make it while (otherCurNode != NULL).

casablanca
Understandable. I recognize that this is a problem, but really doesn't address the real problem at hand in that the constructor for the wrong object is being called. Thanks for the tip though =)
AndyPerfect
+1  A: 

So, instead of calling the copy constructor for ll2, the copy constructor for ll is being called, and ll2 is being set to the same reference as ll.

You can make sure this is not what happening. Your ll and ll2 variables (BTW you can see why using lowercase l in short names is never a good idea) are allocated on the stack, they are not references. To see this, open Quick Watch while both variables are in scope, and type &ll, and then &ll2. You will see they have different addresses.

What do your default constructor and assignment operator look like? Are there any other constructors? Any other places where you assign front instance variable?

Alex Emelianov
indeed, I've checked and ensured that ll and ll2 are located in different location in memory. What is interesting though, is that front for both values points to that same location in memory...The location of front for ll is modified during the line LinkedList ll2(ll); This whole situation is leaving me baffled.
AndyPerfect
Looks like you have left your assignment operator to be the default, which is automatically generated by the compiler. It assigns all members, including `front`. This is not the behavior you want.
Alex Emelianov
The rule is, if you implement custom copy constructor, implement the assignment operator as well.
Alex Emelianov
Could this really be an issue? The empty constructor simple sets front and back to null and sets numNodes to 0. That's it. As you may have noticed, the same problem is arising if I simply create ll, then call the copy constructor to ll2. No other funny business. front and back are pointers, so the assignment operator should never be used...or am I mistaken? Also, I have implemented the assignment operator already.
AndyPerfect
Are you sure your assignment operator does not copy `front` as a pointer and makes a deep copy instead? As a side question, how do you handle code duplication between copy constructor and the assignment?
Alex Emelianov
One more thing... once an object is constructed, you cannot call a copy constructor for it. One constructor per instance. That probably explains why assignment operator gets into play. Can I see the method signature you are using when implementing it?
Alex Emelianov
In the code shown, operator=() is never going to be called.
pkh