NOTE: Study this page carefully! A very large number of catastrophic errors made by Java programmers when they move to C++ are caused by not understanding the concepts described here!
Unlike Java,
instances of classes ("objects") can be allocated on the stack
in C++. That is, you do not need to use new
to allocate an
instance of an object. Version 2 of our class Complex at the bottom of the
Classes page created c1 and c2 on the stack. (They were allocated on
the heap – like Java – in Version 1.)
Among other things, the ability to create stack-allocated objects in C++ means that the following code – which is syntactically valid in both C++ and Java – causes quite different actions to be taken depending on whether it is compiled and executed as Java or C++ code.
void reportGrievanceAgainst(Employee b) // Line 1 { Employee x; // Line 2 x = b; // Line 3 b.logTheGrievance(); // Line 4 }
Let's assume that Employee
has been defined as a class. We will study this
simple method line by line.
|
|
|
|
The prototype declares that the formal parameter 'b ' is a reference to the
instance of Employee that was passed as the actual parameter in the
calling environment. Thus if we modify 'b ' in the body of
reportGrievanceAgainst , we
will be changing that actual parameter. |
The prototype declares that the formal parameter 'b ' is a stack-allocated instance
of Employee local to the reportGrievanceAgainst function. When the
function is called, 'b ' is initialized with a copy of the
Employee object passed as the actual parameter in the calling environment.
(That is, the Employee instance is "passed by value"
as discussed on the Parameter passing page.)
Therefore if we modify 'b ' in the body of
reportGrievanceAgainst , we will modify the copy, not the actual parameter in the
calling environment. |
|
The "Employee x; " statement causes a reference
variable to be allocated. No actual Employee instance is created,
and x is not initialized to point to
anything (not even null ). |
The "Employee x; " statement causes a different instance of an Employee (named 'x ')
to be allocated on the runtime stack and its no-parameter constructor to
be invoked. The resulting instance is thus local (not
heap-allocated), and it will be deleted upon exit
from reportGrievanceAgainst. |
|
The Java code generated for the statement "x=b; " would simply
make 'x ' and 'b ' refer to the same Employee instance.
Each is now a reference to the
instance of Employee that was passed as the actual parameter in the
calling environment.
|
The C++ code generated for the statement "x=b; " causes a
copy of the Employee data in 'b ' to be placed in the storage
space allocated for the (local) Employee instance named 'x '.
Although the two now have the same data, they remain independent instances, and
both are independent of the actual parameter in the calling environment. |
|
The method "logTheGrievance " presumably modifies the data stored in the
Employee instance so that the grievance is actually stored.
Since 'b ' references the actual
parameter in the calling environment, the grievance actually gets logged in the data associated
with the actual parameter in the calling environment. The end result would have been identical
if line 4 were replaced by: x.logTheGrievance();
|
Since we are dealing with three independent instances of Employee , the changes made
to 'b ' are only made to that one instance. Both the local 'x ' and
the actual parameter in the calling environment continue to hold (separate copies of) the original
data as passed to reportGrievanceAgainst .
|
Clearly the employee in question is hoping that this code was written by a naive C++ programmer!
The following table shows how another method could call reportGrievanceAgainst. The code in both columns of the table below is syntactically valid when interpreted as Java. By contrast, only the code in the C++ column would compile without errors by a C++ compiler. (Why? There are two reasons.)
Note also: Even though the code in the C++ column is syntactically valid when interpreted as Java, most Java
compilers would detect that an uninitialized variable is being passed to reportGrievanceAgainst
and would generate a compilation error.
|
|
void processTransactions( ) { Employee fred = new Employee( ); reportGrievanceAgainst(fred); } |
void processTransactions( ) { Employee fred; reportGrievanceAgainst(fred); } |