3

So I was going through a piece of text in C++ and came across the following piece of code:

class example
{
    int dataMember;

public:
    example& assign(const example& source)
    {
        if(this!=&source)
        {
            this->~example();
            new (this) example(source);
        }


    }

};

Okay so I am trying to decode what this function assign is doing. What I have understood yet:

  1. The function takes a constant reference of instance of the class and returns a reference to the class.

  2. Inside the if block, firstly the destructor is called for the current instance (As far as I know, current object is destroyed and memory is freed).

Now the main question:

new (this) example(source)

This line is troubling me. What is happening here? If I am asked to guess, I would say that a new object is being created and is assigned as the current object, as I can infer from this keyword.

Can anyone clear this up? How exactly are things going on here?

Is this kind of method safe? (If allocation is happening dynamically, programmer will have to deallocate it in future manually)

Thanks.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
Akshay Arora
  • 1,953
  • 1
  • 14
  • 30
  • 3
    it's called [placement new](http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new) – bolov Jan 31 '15 at 14:20
  • @bolov, awesome, this is the exact thing I was looking for. Now I will search online and read more about it. – Akshay Arora Jan 31 '15 at 14:24
  • 1
    This is a horrible code. – milleniumbug Jan 31 '15 at 14:32
  • 1
    @bolov, The object might not have static lifetime. This object could be anywhere, with any lifetime. Lifetime is not the issue here. – Aaron McDaid Jan 31 '15 at 14:32
  • 1
    @AaronMcDaid yes, you are right, I should have said that this new doesn't allocate any memory and therefore it doesn't add the need of a new delete. – bolov Jan 31 '15 at 14:42

2 Answers2

3

What you are seeing is an attempt at code-reusage. The idea is to use the copy constructor to implement the assignment operator (or, in this case an assignment function).

First, to get rid of the easy stuff:

  • The if ensures that a self assignment (e.g. x.assign(x)) is being handled correctly. This is necessary since the implementation relies on the fact that changing *this does not change source. By comparing this to the address of the other object, this does test the objects for equality, but for sameness.

  • The function is missing a return *this;, but you probably noticed that already.

Now to the two remaining lines:

this->~example();

Explicitly calls the destructor of the class example. After that line the this pointer no longer points to an object, but to uninitialized memory of sizeof(example).

new (this) example(source);

Is the so-called placement new, which does not allocate memory, but simply creates a new example at the location pointed to by this by calling its copy constructor. In a way this is the syntax to explicitly call a constructor w/o allocating any memory.

Note that this reuses the memory that *this held previously, no matter where that is: It could even be e.g. a virtual base of an element of a dynamically allocated array...

As to the safety: In practice this is safe (although ugly and possibly not very efficient) as long as neither the constructor nor destructor throw. However, you can run into problems when deriving from a class that attempts to be sneaky like that.

If you want to know the simplest way to write an assignment operator/function that reuses the copy constructor, check this question, which basically boils down to:

example& assign(example source)
{
    swap(*this, source);
    return *this;
}

The most important differences:

  • It utilizes a move constructor where applicable.
  • This code is exception safe if the copy/move constructors and swap are exception safe (which they should always be).
  • It is slightly less efficient for self-assignment, since it will perform the copy, instead of recognizing a self-assignment.
Community
  • 1
  • 1
danielschemmel
  • 10,885
  • 1
  • 36
  • 58
  • Can you expand on your last comment, about derived classes? Are you referring to the call to the destructor in this, or the call to the placement-new constructor? Or both? – Aaron McDaid Jan 31 '15 at 14:37
  • @AaronMcDaid The placement-new will [slice](http://stackoverflow.com/questions/274626/what-is-object-slicing) and if the destructor is `virtual`, well, you will destroy more than you expect. Also, it might have shared `virtual` base classes... – danielschemmel Jan 31 '15 at 14:38
  • @AaronMcDaid Basically: I would not have the balls to rely on code that derives from such a class, no matter if it seems to work or not. (This only applies to deriving from it, not using composition.) – danielschemmel Jan 31 '15 at 14:40
  • Understood, and agreed that this is too risky to use (as is) in serious code. But now I'm curious how to fix it! This class is called `example`. If we have a `class derived : public example`, and if the destructor was `virtual`, then the call to the destructor here would destroy all of the `derived` object, while the placement new would only reconstruct a part of the object. Which would leave the object pointed to by `derived *x ` in a messed-up state. But, is it possible to call the destructor on the correct slice, somehow bypassing its `virtual` nature? – Aaron McDaid Jan 31 '15 at 14:53
  • 2
    @AaronMcDaid Yeah - just don't... :) – danielschemmel Jan 31 '15 at 14:56
1

This part destroys the object:

this->~example();

It doesn't release the memory though, except for memory that is released due to the destructor call. Then, it proceeds with an invocation of "placement new":

new (this) example(source);

This will construct an object on the (unreleased) memory of the former object.

Concerning the safety of this, it is at least dangerous. If after destroying the object you fail to create the new object, you have a weird zombie object that will get destroyed a second time (causing undefined behaviour) when its normal lifetime ends.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • Will I then have to explicitly call the destructor to make sure there is no memory leak? – Akshay Arora Jan 31 '15 at 14:27
  • 1
    No. Normally, the scope creates one call to the constructor and one to the destructor. Now, this code injects a destructor call followed by a constructor call in between. In summary, you have two constructor/destructor pairs. – Ulrich Eckhardt Jan 31 '15 at 14:29