• Home   /  
  • Archive by category "1"

Assignment Operator With Pointers In Java

The Copy Constructor Definition

The definition of the copy constructor (the actual code for the function) should be put in a ".C" file, along with the code for the other class member functions. The copy constructor should copy the values of all non-pointer data members, and should copy the objects pointed to by all pointer data members (this is sometimes called a deep copy). For example, the copy constructor for the IntList class should perform the following tasks:

  1. allocate a new array of ints of size L.arraySize (L is the copy constructor's IntList parameter); set Items to point to the new array;
  2. copy the values in the array pointed to by L.Items to the new array;
  3. initialize the numItems and arraySize fields to have the same values as the ones in L.numItems and L.arraySize.
Here is the code for the IntList copy constructor (note that, like the other constructor functions, the copy constructor can use a member initialization list to initialize data members, as well as using code in the body of the function):
    IntList::IntList(const IntList & L): Items(new int[L.arraySize]), numItems(L.numItems), arraySize(L.arraySize) { for (int k=0; k<numItems; k++) { Items[k] = L.Items[k]; } }

Operator=

In C++ you can assign from one class object to another (of the same type). For example:

    IntList L1, L2; ... L1 = L2; // this assignment is OK
By default, class assignment is just field-by-field assignment (i.e., a shallow copy is done). For example, the above assignment is equivalent to:
    L1.Items = L2.Items; L1.numItems = L2.numItems; L1.arraySize = L2.arraySize;
(Of course, the three field assignments could not be written outside an IntList member function, since they are private fields; however, they illustrate the effect of the assignment L1 = L2.)

If a class includes pointer fields, the default class assignment causes aliasing, and as we have seen in the case of the copy constructor, this can lead to trouble! For example, if the L2.Items array is full when the assignment is done, then a subsequent call to L1.AddToFront will cause the array to be returned to free storage (so L2.Items will become a dangling pointer).

The default assignment can also cause storage leaks when the class has a pointer field. For example, when L1 = L2; is executed, L1.Items is simply overwritten with the value in L2.Items, the array that L1 was pointing to is not returned to free storage (and that storage is now lost).

To prevent these problems, you should always define operator= as a class member function for a class with a pointer field, and you should define operator= to do a deep copy. The declaration of the member function looks like this for the IntList class:

    IntList & operator=(const IntList &L);
The idea is that when the assignment L1 = L2; is executed, L1's member function operator= is called, and L2 is passed as the argument to that function.

Note that IntList's operator= function returns an IntList. This is to permit chained assignment, for example: L1 = L2 = L3;. When this statement is executed, the expression L2 = L3 is evaluated first; the result of evaluating that expression is used as the right-hand side of the assignment to L1. The operator= function returns its result by reference (that's what the ampersand means). This is done for efficiency, to prevent the IntList copy constructor being called to make a copy of the returned value.

Note that operator= differs from the copy constructor in three important ways:

  1. The object being assigned to has already been initialized; therefore, if it has a pointer field, the storage pointed to must be freed to prevent a storage leak.
  2. It is possible for a programmer to assign from a variable into itself; for example: L1 = L1. The operator= code must check for this case, and do nothing.
  3. The operator= code must return a value.

Here is the definition of operator= for the IntList class:

    IntList & IntList::operator=(const IntList &L) { // check for "self assignment" and do nothing in that case if (this == &L) return *this; else { delete [] Items; // free the storage pointed to by Items Items = new int[L.arraySize]; // allocate a new array arraySize = L.arraySize; // set the arraySize field // copy the items from L's array to the new array // also sets the numItems field for (numItems=0; numItems < L.numItems; numItems++) { Items[numItems] = L.Items[numItems]; } return *this; // return this IntList }
Note that, as in Java, every member function has access to a variable named this that is a pointer to the object whose member function was called. So for example, when L1 = L2; is executed, L1's member function operator= is called, so this is a pointer to L1.

To check whether the assignment was L1 = L1, we compare the pointer this with the address of the parameter, L; in the case of L1 = L1, the parameter is L1, so its address is the same as the address that is the value of this. Be sure to include this test every time you write an operator= function!

We also make use of this for the returned value; the type to be returned is IntList (not pointer to IntList) so we return *this (the IntList pointed to by this) rather than plain this.

Wrap-up

Every class that has a pointer data member should include the following member functions:

  1. a destructor,
  2. a copy constructor,
  3. operator= (assignment)
If you don't write a destructor, your code will probably still work, but it may have storage leaks (some uses of the new operator will have no corresponding use of delete).

If you don't write a copy constructor, or you don't write operator=, your code may not work correctly; there may be attempts to dereference dangling pointers (which may cause runtime errors, or may cause garbage values to be assigned to some variables), and/or some data may be lost or corrupted.

A kind of compromise is to forbid the use of the copy constructor and the assignment of two class objects. You can do this by declaring the copy constructor and operator= as private member functions (just declaring them is enough; you do not need to write the actual code). In this case, any code that would normally cause the copy constructor or operator= to be called will instead cause a compile-time error.

Solutions to Self-Study Questions

Test Yourself #1

A reference parameter's destructor function is not called at the end of the function because the corresponding actual parameter refers to the same object. The object does not "go away" when the function ends, so its dynamically allocated storage should not be freed.

Test Yourself #2

Here are actual parameter S and formal parameter L at the beginning of function f:

    +-------------+ | +---+ | +---------+ +---------+ +---------+ S: | head: | ------->| "a" | ---->| "b" | ---->| "c" | \ | | +---+ | +---------+ +---------+ +---------+ +-------------+ ^ | +-------------+ | | +---+ | | L: | head: | --------+ | +---+ | +-------------+

And here are S and L just after the call to Lookup in function f:

    +-------------+ | +---+ | +---------+ +---------+ S: | head: | ------->| "a" | ---->| "c" | \ | | +---+ | +---------+ +---------+ +-------------+ ^ | +-------------+ +-----------+ | +---+ | +---------+ | L: | head: | ------->| "b" | ----+ | +---+ | +---------+ +-------------+

Because the StrList class has no copy constructor, the head fields of both S and L point to the same linked-list. The call to Lookup changes that list (by moving the node that contains "b" to the front of the list); it also changes the head field of L to point to the node that was moved. However, it doesn't know anything about S, so S's head field still points to the node that contains "a" (and the node that contains "b" has effectively been removed from the list represented by S).

Section 3 -- Study Questions

These study questions cover review basic features of pointers. Two of the questions make heavy use of memory drawings. Memory drawings are an excellent way to think through pointer problems.

Question 1

At the end of the above code, is set to have a pointee and then dereferenced it store the number 13 into its pointee. After this happens, what is the value of 's pointee?

Answer: The value of 's pointee is 13 because it is also 's pointee. This is what sharing is all about -- multiple pointers pointing to one pointee.

Question 2

Consider the following drawing...

Using the language of your choice, write some code that creates the above pointer structure.

Answer: The basic steps are...

  1. Allocate two pointers.
  2. Allocate two pointees and set the pointers to point to them.
  3. Store the numbers 1 and 2 into the pointees.
  4. Assign the first pointer to point to the second pointee. This "loses" the reference to the first pointee which is unusual, but that's what the question calls for.
C CodeJava Code
{ int* x; int* y; x = malloc(sizeof(int)); y = malloc(sizeof(int)); *x = 1; *y = 2; x = y;}{ IntObj x; IntObj y; x = new IntObj(); y = new IntObj(); x.value = 1; y.value = 2; x = y;}

Question 3

Suppose you have a pointee type called "Node" which contains two things: an int, and a pointer to another Node (the declaration for such a Node type is given below). With such a pointee type, you could arrange three Node pointees in a structure where they were pointing to each other like this...

The pointer named points to the first Node pointee. The first Node contains a pointer to the second, the second contains a pointer to the third, and the third contains a pointer back to the first. This structure can be build using only the rules of pointee allocation, dereferencing, and assignment that we have seen. Using the declaration below, each Node contains an integer named and a pointer to another Node named .

C CodeJava Code
struct Node { int value; struct Node* next;};class Node { public int value; public Node next;};

Write the code to build the structure in the above drawing. For convenience, you may use temporary pointers in addition to . The only new syntax required is that in C, the operator dereferences a pointer to access a field in the pointee -- so accesses the field named in 's pointee.

AnswerThe basic steps are...

  1. Allocate three pointers: x for the first Node, and temporary pointers and for the other two Nodes.
  2. Allocate three Node pointees and store references to them in the three pointers.
  3. Dereference each pointer to store the appropriate number into the field in its pointee.
  4. Dereference each pointer to access the field in its pointee, and use pointer assignment to set the field to point to the appropriate Node.
C CodeJava Code
{ // Allocate the pointers struct Node* x; struct Node* y; struct Node* z; // Allocate the pointees x = malloc(sizeof(Node)); y = malloc(sizeof(Node)); z = malloc(sizeof(Node)); // Put the numbers in the pointees x->value = 1; y->value = 2; z->value = 3; // Put the pointers in the pointees x->next = y; y->next = z; z->next = x;}{ // Allocate the pointers Node x; Node y; Node z; // Allocate the pointees x = new Node(); y = new Node(); z = new Node(); // Put the numbers in the pointees x.value = 1; y.value = 2; z.value = 3; // Put the pointers in the pointees x.next = y; y.next = z; z.next = x;}

The Node structure introduced here is actually a real data type used to build the "linked list" data structure. Linked lists are a realistic applied use of pointers and are an excellent area to develop your pointer skills. See Linked List Basics and Linked List Problems in the Stanford CS Education Library for lots of linked list material.

Postscript

Copyright Nick Parlante, 1999. This material may be copied and redistributed so long as the standard Stanford CS Education Library notice on the first page is retained: "This is document 106 in the Stanford CS Education Library. This and other free materials are available at cslibrary.stanford.edu."

I hope that you benefit from this material in the spirit of goodwill in which it is given. That someone seeking education should have the opportunity to find it.


Up to the CS Education Library Home

One thought on “Assignment Operator With Pointers In Java

Leave a comment

L'indirizzo email non verrĂ  pubblicato. I campi obbligatori sono contrassegnati *