9

In his new book TC++PL4, Stroustrup casts a slightly different light on a once usual practice regarding user-controlled memory allocation and placement new—or, more specifically, regarding the enigmatical "placement delete." In the book's sect. 11.2.4, Stroustrup writes:

The "placement delete" operators do nothing except possibly inform a garbage collector that the deleted pointer is no longer safely derived.

This implies that sound programming practice will follow an explicit call to a destructor by a call to placement delete.

Fair enough. However, is there no better syntax to call placement delete than the obscure

::operator delete(p);

The reason I ask is that Stroustrup's sect. 11.2.4 mentions no such odd syntax. Indeed, Stroustrup does not dwell on the matter; he mentions no syntax at all. I vaguely dislike the look of ::operator, which interjects the matter of namespace resolution into something that properly has nothing especially to do with namespaces. Does no more elegant syntax exist?

For reference, here is Stroustrup's quote in fuller context:

By default, operator new creates its object on the free store. What if we wanted the object allocated elsewhere?... We can place objects anywhere by providing an allocator function with extra arguments and then supplying such extra arguments when using new:

void* operator new(size_t, void* p) { return p; }

void buf = reinterpret_cast<void*>(0xF00F);
X* p2 = new(buf) X;

Because of this usage, the new(buf) X syntax for supplying extra arguments to operator new() is known as the placement syntax. Note that every operator new() takes a size as its first argument and that the size of the object allocated is implicitly supplied. The operator new() used by the new operator is chosen by the usual argument-matching rules; every operator new() has a size_t as its first argument.

The "placement" operator new() is the simplest such allocator. It is defined in the standard header <new>:

void* operator new (size_t, void* p) noexcept;
void* operator new[](size_t, void* p) noexcept;

void* operator delete (void* p, void*) noexcept; // if (p) make *p invalid
void* operator delete[](void* p, void*) noexcept;

The "placement delete" operators do nothing except possibly inform a garbage collector that the deleted pointer is no longer safely derived.

Stroustrup then continues to discuss the use of placement new with arenas. He does not seem to mention placement delete again.

Community
  • 1
  • 1
thb
  • 13,796
  • 3
  • 40
  • 68
  • 2
    Can you give a more complete quote? You might be confusing a *function* that happens to be called "operator" with an actual *operator*. There is no placement-delete *expression* in C++. – Kerrek SB Jun 30 '13 at 16:16
  • 2
    There's only one kind of `delete expression` in C, and it doesn't allow any placement syntax (just look at the grammar productions in the standard). That's what many reasonably skilled C++ programmers mean when they say "there's no *placement delete expression* in C++". Perhaps there will be something to this effect in C++14 or C++17, but now there's nothing. – n. m. could be an AI Jun 30 '13 at 16:40
  • @KerrekSB: I have now given a more complete quote. – thb Jun 30 '13 at 16:46
  • @n.m.: If you cared to make your enlightening comment an *answer,* naturally I would upvote it. – thb Jun 30 '13 at 16:57
  • To be frank, I find the tone of this question a little discouraging: In your first sentence, you come out of nowhere and claim that the very creator of the language is contradicting himself about the language. A *lot* of thought has gone into C++, so that's a pretty strong claim. – Kerrek SB Jun 30 '13 at 17:33
  • OK, after reading the actual text, it is clear that this has nothing to do with C++11 or 14, and has always been the case in C++ since day 1. The question title is misleading, and should better be something like "placement forms of the `operator delete` functions". Note that Stroustrup puts "placement delete" in quotation marks, and this isn't mean as any kind of formal definition, but merely a colloquialism. (I personally don't like the way he phrased it, by the way, as it could have been done a little less confusingly.) – Kerrek SB Jun 30 '13 at 17:40
  • He probably just assumed you would think to call the dtor directly, e.g. `class Foo* ptr = inplaceFoo(); ptr->~Foo();`. Calling "delete" is redundant for any other purpose than invoking the dtor, so why bother adding the middleman? – kfsone Jun 30 '13 at 21:49
  • 1
    The interesting thing is that the placement new operator does nothing, too (well, it returns its second argument). So I'm not sure why he called out placement delete on this specially. It might be more interesting to note that calling placement new is supported by the `new` expression syntax, while calling placement delete is not supported by the `delete` expression syntax (as mentioned in the question). Placement delete can only be called explicitly or it gets called implicitly when the constructor that runs as a result of a placement new operation throws an exception. – Michael Burr Jul 01 '13 at 18:39
  • 2
    Maybe allowing for some form of calling a constructor directly (with a memory address provided) would have resulted in fewer confusion. Because that's what placement new/delete is really about -- calling the constructor and destructor. Nothing is allocated or freed. – Damon Jul 01 '13 at 20:21
  • @KerrekSB: Good point. I have changed the question's title and first sentence per your suggestion. – thb Jul 02 '13 at 15:35
  • @Damon: +1. Here is what is weird, then: placement delete does not call a destructor. – thb Jul 02 '13 at 19:44
  • @thb: Placement delete _is the act of calling the destructor_, manually. There is no such thing as placement delete in C++. When people talk of placement delete, they mean calling the destructor (something you _normally_ never do because `delete` does it for you!). That's what is so confusing. Placement new calls the constructor for you, since there is no way of calling the constructor otherwise. But of course invoking `delete ptr;` doesn't give a clue whether that's a normal or placement-delete. So instead, you call the destructor. But it's confusing to people nevertheless. – Damon Jul 03 '13 at 11:35

3 Answers3

2

First of all: No there isn't.

But what is the type of memory? Exactly, it doesn't have one. So why not just use the following:

typedef unsigned char byte;

byte *buffer = new byte[SIZE];

Object *obj1 = new (buffer) Object;
Object *obj2 = new (buffer + sizeof(Object)) Object;
...
obj1->~Object();
obj2->~Object();

delete[] buffer;

This way you don't have to worry about placement delete at all. Just wrap the whole thing in a class called Buffer and there you go.

EDIT

I thought about your question and tried a lot of things out but I found no occasion for what you call placement delete. When you take a look into the <new> header you'll see this function is empty. I'd say it's just there for the sake of completeness. Even when using templates you're able to call the destructor manually, you know?

class Buffer
{
    private:
        size_t size, pos;
        byte *memory;

    public:
        Buffer(size_t size) : size(size), pos(0), memory(new byte[size]) {}

        ~Buffer()
        {
            delete[] memory;
        }

        template<class T>
        T* create()
        {
            if(pos + sizeof(T) > size) return NULL;
            T *obj = new (memory + pos) T;
            pos += sizeof(T);
            return obj;
        }

        template<class T>
        void destroy(T *obj)
        {
            if(obj) obj->~T(); //no need for placement delete here
        }
};


int main()
{
    Buffer buffer(1024 * 1024);

    HeavyA *aObj = buffer.create<HeavyA>();
    HeavyB *bObj = buffer.create<HeavyB>();

    if(aObj && bObj)
    {
        ...
    }

    buffer.destroy(aObj);
    buffer.destroy(bObj);
}

This class is just an arena (what Stroustrup calls it). You can use it when you have to allocate many objects and don't want the overhead of calling new everytime. IMHO this is the only use case for a placement new/delete.

Philip
  • 271
  • 1
  • 15
  • You're right. That's more or less what I have been doing. What I wanted to learn today however is what Stroustrup means when he seems to suggest that both you and I start doing it differently. – thb Jun 30 '13 at 16:55
2

If you don't want to use ::, you don't really have to. In fact, you generally shouldn't (don't want to).

You can provide replacements for ::operator new and ::operator delete (and the array variants, though you should never use them).

You can also, however, overload operator new and operator delete for a class (and yes, again, you can do the array variants, but still shouldn't ever use them).

Using something like void *x = ::operator new(some_size); forces the allocation to go directly to the global operator new instead of using a class specific one (if it exists). Generally, of course, you want to use the class specific one if it exists (and the global one if it doesn't). That's exactly what you get from using void *x = operator new(some_size); (i.e., no scope resolution operator).

As always, you need to ensure that your news and deletes match, so you should only use ::operator delete to delete the memory when/if you used ::operator new to allocate it. Most of the time you shouldn't use :: on either one.

The primary exception to that is when/if you're actually writing an operator new and operator delete for some class. These will typically call ::operator new to get a big chunk of memory, then divvy that up into object-sized pieces. To allocate that big chunk of memory, it typically (always?) has to explicitly specify ::operator new because otherwise it would end up calling itself to allocate it. Obviously, if it specifies ::operator new when it allocates the data, it also needs to specify ::operator delete to match.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
2

This implies that sound programming practice will follow an explicit call to a destructor by a call to placement delete.

No it doesn't. IIUC Stroustrup does not mean placement delete is necessary to inform the garbage collector that memory is no longer in use, he means it doesn't do anything apart from that. All deallocation functions can tell a garbage colector memory is no longer used, but when using placement new to manage memory yourself, why would you want a garbage collector to fiddle with that memory anyway?

I vaguely dislike the look of ::operator, which interjects the matter of namespace resolution into something that properly has nothing especially to do with namespaces.

"Properly" it does have to do with namespaces, qualifying it to refer to the "global operator new" distinguishes it from any overloaded operator new for class types.

Does no more elegant syntax exist?

You probably don't ever want to call it. A placement delete operator will be called by the compiler if you use placement new and the constructor throws an exception. Since there is no memory to deallocate (because the pacement new didn't allocate any) all it does it potentially mark the memory as unused.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521