Looking Again at Placement new

Recall that placement new allows you to specify the memory location used to allocate memory. Chapter 9, “Memory Models and Namespaces,” discusses placement new in the context of built-in types. Using placement new with objects adds some new twists. Listing 12.8 uses placement new along with regular new to allocate memory for objects. It defines a class with a chatty constructor and destructor so that you can follow the history of objects.

Listing 12.8. placenew1.cpp


// placenew1.cpp  -- new, placement new, no delete
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
    string words;
    int number;
public:
    JustTesting(const string & s = "Just Testing", int n = 0)
    {words = s; number = n; cout << words << " constructed "; }
    ~JustTesting() { cout << words << " destroyed ";}
    void Show() const { cout << words << ", " << number << endl;}
};
int main()
{
    char * buffer = new char[BUF];       // get a block of memory

    JustTesting *pc1, *pc2;

    pc1 = new (buffer) JustTesting;      // place object in buffer
    pc2 = new JustTesting("Heap1", 20);  // place object on heap

    cout << "Memory block addresses: " << "buffer: "
        << (void *) buffer << "    heap: " << pc2 <<endl;
    cout << "Memory contents: ";
    cout << pc1 << ": ";
    pc1->Show();
    cout << pc2 << ": ";
    pc2->Show();

    JustTesting *pc3, *pc4;
    pc3 = new (buffer) JustTesting("Bad Idea", 6);
    pc4 = new JustTesting("Heap2", 10);

    cout << "Memory contents: ";
    cout << pc3 << ": ";
    pc3->Show();
    cout << pc4 << ": ";
    pc4->Show();

    delete pc2;                          // free Heap1
    delete pc4;                          // free Heap2
    delete [] buffer;                    // free buffer
    cout << "Done ";
    return 0;
}


The program in Listing 12.8 uses new to create a memory buffer of 512 bytes. It then uses new to create two objects of type JustTesting on the heap and attempts to use placement new to create two objects of type JustTesting in the memory buffer. Finally, it uses delete to free the memory allocated by new. Here is the output:

Just Testing constructed
Heap1 constructed
Memory block addresses:
buffer: 00320AB0    heap: 00320CE0
Memory contents:
00320AB0: Just Testing, 0
00320CE0: Heap1, 20
Bad Idea constructed
Heap2 constructed
Memory contents:
00320AB0: Bad Idea, 6
00320EC8: Heap2, 10
Heap1 destroyed
Heap2 destroyed
Done

As usual, the formatting and exact values for the memory addresses will vary from system to system.

There are a couple problems with placement new as used in Listing 12.8. First, when creating a second object, placement new simply overwrites the same location used for the first object with a new object. Not only is this rude, it means that the destructor was never called for the first object. This, of course, would create real problems if, say, the class used dynamic memory allocation for its members.

Second, using delete with pc2 and pc4 automatically invokes the destructors for the two objects that pc2 and pc4 point to. But using delete [] with buffer does not invoke the destructors for the objects created with placement new.

One lesson to be learned here is the same lesson you learned in Chapter 9: It’s up to you to manage the memory locations in a buffer that placement new populates. To use two different locations, you provide two different addresses within the buffer, making sure that the locations don’t overlap. You can, for example, use this:

pc1 = new (buffer) JustTesting;
pc3 = new (buffer + sizeof (JustTesting)) JustTesting("Better Idea", 6);

Here the pointer pc3 is offset from pc1 by the size of a JustTesting object.

The second lesson to be learned here is that if you use placement new to store objects, you need to arrange for their destructors to be called. But how? For objects created on the heap, you can use this:

delete pc2;   // delete object pointed to by pc2

But you can’t use this:

delete pc1;   // delete object pointed to by pc1? NO!
delete pc3;   // delete object pointed to by pc3? NO!

The reason is that delete works in conjunction with new but not with placement new. The pointer pc3, for example, does not receive an address returned by new, so delete pc3 throws a runtime error. The pointer pc1, on the other hand, has the same numeric value as buffer, but buffer is initialized using new [], so it’s freed using delete [], not delete. Even if buffer were initialized by new instead of new [], delete pc1 would free buffer, not pc1. That’s because the new/delete system knows about the 256-byte block that is allocated, but it doesn’t know anything about what placement new does with the block.

Note that the program does free the buffer:

delete [] buffer;                    // free buffer

As this comment suggests, delete [] buffer; deletes the entire block of memory allocated by new. But it doesn’t call the destructors for any objects that placement new constructs in the block. You can tell this is so because this program uses chatty destructors, which report the demise of "Heap1" and "Heap2" but which remain silent about "Just Testing" and "Bad Idea".

The solution to this quandary is that you must call the destructor explicitly for any object created by placement new. Normally, destructors are called automatically; this is one of the rare cases that require an explicit call. An explicit call to a destructor requires identifying the object to be destroyed. Because there are pointers to the objects, you can use these pointers:

pc3->~JustTesting();  // destroy object pointed to by pc3
pc1->~JustTesting();  // destroy object pointed to by pc1

Listing 12.9 fixes Listing 12.8 by managing memory locations used by placement new and by adding appropriate uses of delete and of explicit destructor calls. One important fact is the proper order of deletion. The objects constructed by placement new should be destroyed in order opposite that in which they were constructed. The reason is that, in principle, a later object might have dependencies on an earlier object. And the buffer used to hold the objects should be freed only after all the contained objects are destroyed.

Listing 12.9. placenew2.cpp


// placenew2.cpp  -- new, placement new, no delete
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;

class JustTesting
{
private:
    string words;
    int number;
public:
    JustTesting(const string & s = "Just Testing", int n = 0)
    {words = s; number = n; cout << words << " constructed "; }
    ~JustTesting() { cout << words << " destroyed ";}
    void Show() const { cout << words << ", " << number << endl;}
};
int main()
{
    char * buffer = new char[BUF];       // get a block of memory

    JustTesting *pc1, *pc2;

    pc1 = new (buffer) JustTesting;      // place object in buffer
    pc2 = new JustTesting("Heap1", 20);  // place object on heap

    cout << "Memory block addresses: " << "buffer: "
        << (void *) buffer << "    heap: " << pc2 <<endl;
    cout << "Memory contents: ";
    cout << pc1 << ": ";
    pc1->Show();
    cout << pc2 << ": ";
    pc2->Show();

    JustTesting *pc3, *pc4;
// fix placement new location
    pc3 = new (buffer + sizeof (JustTesting))
                JustTesting("Better Idea", 6);
    pc4 = new JustTesting("Heap2", 10);

    cout << "Memory contents: ";
    cout << pc3 << ": ";
    pc3->Show();
    cout << pc4 << ": ";
    pc4->Show();

    delete pc2;           // free Heap1
    delete pc4;           // free Heap2
// explicitly destroy placement new objects
    pc3->~JustTesting();  // destroy object pointed to by pc3
    pc1->~JustTesting();  // destroy object pointed to by pc1
    delete [] buffer;     // free buffer
    cout << "Done ";
    return 0;
}


Here is the output of the program in Listing 12.9:

Just Testing constructed
Heap1 constructed
Memory block addresses:
buffer: 00320AB0    heap: 00320CE0
Memory contents:
00320AB0: Just Testing, 0
00320CE0: Heap1, 20
Better Idea constructed
Heap2 constructed
Memory contents:
00320AD0: Better Idea, 6
00320EC8: Heap2, 10
Heap1 destroyed
Heap2 destroyed
Better Idea destroyed
Just Testing destroyed
Done

The program in Listing 12.9 places the two placement new objects in adjacent location and calls the proper destructors.

Reviewing Techniques

By now, you’ve encountered several programming techniques for dealing with various class-related problems, and you may be having trouble keeping track of all of them. So the following sections summarize several techniques and when they are used.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.179.100