Please review the following as needed.
Java programs create all objects on the heap, and, with few exceptions, programmers always create them with the new
operator. C++ programs can create objects on the stack or the heap, giving programmers more options. The code programmers write to instantiate objects on the heap is similar in both languages, and the abstract views of the objects in memory are the same.
Java | C++ |
---|---|
public class Widget { private int color = 0xff0000; // (a) private double cost = 0.0; public Widget(){} // (b) Widget(int aColor, double aCost) // (c) { color = aColor; cost = aCost; } public int getColor() { return color; } //(e) } Widget w1 = new Widget(); // (f) Widget w2 = new Widget(0x00ff00, 5.99) // (g) |
class widget { private: int color = 0xff0000; // (a) double cost = 0.0; public: widget(){} // (b) widget(int a_color, double a_cost) // (c) : color(a_color), cost(a_cost) {} ~widget() { . . . } // (d) int get_color() { return color; } // (e) }; widget* w1 = new widget; // (f) widget* w2 = new widget(0x00ff00, 5.99); // (g) |
Abstract Representation | |
new
operator. Creating single objects on the heap with the new
operators is similar in Java and C++. Java's naming conventions are more stringent than C++'s. So, a C++ program could use the same class, function, and argument names appearing in the Java code.
delete
operator, but they can perform any needed "cleans up" operation. The widget class doesn't have a legitimate task for a constructor (as suggested by the ellipses in the function body), but the example includes one to introduce the syntax. We'll cover destructors in detail in the next chapter.widget* w1 = new widget();
is also correct.When a program creates a single object, the Java and C++ new
statements and the way the objects "look" in memory are nearly identical. But this situation changes when the program creates an arrayof objects. The code remains similar, but its meaning and its effect on memory are quite different.
Java | C++ |
---|---|
Widget w3 = new Widget[n]; |
widget* w3 = new widget[n]; |
(a) | (b) |
new
operator. When we create an array automatically or locally on the stack, we must fully specify the size at compile time - the size must be a compile-time constant, not a variable or more complex expression. But, if we create an array dynamically with the new
operator, we can wait until runtime to determine the array's size. These examples assume that n = 3.
Another striking difference is how the two languages discard unneeded objects. The previous examples allocated memory on the heap with new
for objects w1, w2, and w3. The program must deallocate the memory and return it to the heap when the objects are no longer needed. Java provides an automatic garbage collector but C++ does not. There are advantages and disadvantages to both approaches. Java's automatic garbage collector takes much longer to run than does C++'s more precise delete
operation, but C++'s explicit deallocation is more error-prone than Java's automatic deallocation.
widget* w1; widget* w2; widget* w3; |
delete w1;
delete w2;
delete[] w3; |
(a) | (b) |
delete
operator.
delete
operator. w1 and w2 each point to a single object, but although variable w3 looks like the other two pointers, it points to an array of objects. The brackets following the delete
operator signal the deletion of an array of objects to the compiler. This information allows the compiler to generate the code necessary to call the destructor, ~Widget()
, for each object in the array; without the brackets, the compiler only calls the destructor once.
Figure 2 illustrates that Java programs create arrays of objects in a multi-step process. The program creates an array of pointers and then creates each object in the array one at a time. Creating an array of pointers in a C++ program is also possible. Furthermore, making the objects one at a time provides another advantage of dynamic memory over automatic variables. Suppose that a program requires a large array of objects. If the array is too small, the program will not have enough objects to complete its tasks, so programmers tend to overestimate the number of objects the program needs. But creating an overly large array of objects using the syntax of Figure 2(b) can result in two problems:
Allocating an array of pointers will also waste memory for the unused pointers, but pointers are relatively small, typically much smaller than large objects. Furthermore, they don't incur the expense of a constructor call. So, allocating objects individually with new
as needed and storing their addresses in an array of pointers allows the program to create the number of objects necessary. Furthermore, the program doesn't call constructors or destructors when pointers are created or destroyed, so this approach also avoids the wasted constructor and destructor calls for unused objects.
Java | C++ |
---|---|
Widget w4 = new Widget[100]; for (int i = 0; i < 3; i++) w4[i] = new Widget(); |
widget* w4[100]; for (int i = 0; i < 3; i++) w4[i] = new widget; |
(a) | (b) |
new
. The program creates the objects one at a time and saves their addresses in the array. The illustration presents an abstract representation of an array of objects in a Java program.Objects stored in arrays still have features (member variables and functions). The following figure illustrates how programs use a combination of the index operator, [], and a selection operator, dot or arrow, to access an object's features. But sometimes, choosing the correct selection operator takes a little thought.
Automatic Array | Dynamic Array | Array of Pointers |
---|---|---|
widget w5[100]; . . . w5[10].get_color(); |
widget* w6 = new widget[100]; . . . w6[10].get_color(); |
widget* w7[100]; for (int i = 0; i < 100; i++) w7[i] = new widget(0x0000ff, 10.0); . . . w7[10]->get_color(); |
(a) | (b) | (c) |