c++constructordestructorcopy-and-swap

Reset an object


Lately I often reset an object by assigning a new value to it with operator=. Most of my classes have a copy constructor and operator= defined using "copy and swap" idiom. Which works fine in most cases, albeit not as efficient as it could be, but that mostly does not matter. There is one case that this does not work though. Its when the destructor needs to be called before the constructor of the new object.

Note: most of the classes for which I use this are uncopyable

class Foo
{
public:
    Foo() : m_i(0) {}
    Foo(int i) : m_i(i) {}

    Foo(Foo&& rhs);
    Foo& operator=(Foo rhs);
    friend void swap(Foo& lhs, Foo& rhs);

private:
    Foo(Foo& rhs) {}    // uncopyable object

    int m_i;
};

Foo::Foo(Foo&& rhs)
: Foo()
{
    swap(*this, rhs);
}

Foo& Foo::operator=(Foo rhs)
{
    swap(*this, rhs);
    return *this;
}

void swap(Foo& lhs, Foo& rhs)
{
    using std::swap;
    swap(lhs.m_i, rhs.m_i);
}

int main()
{
    Foo f(123);
    f = Foo(321);   // at one time both Foo(123) and Foo(321) exist in memory
}

I have then taught to maybe rewrite operator= to first manually call the destructor and then do the swap (in this case rhs would be taken by const reference). However this answer on stackOverflow made me think otherwise.

I really like operator= to reset my objects, becuase the code is clean and is the same as for built in types (like int). It also uses both the code from constructor and destructor, so no extra code needs to be written and maintained.

So my question is: Is there a way to achieve my goal to reset my object with clean code and no extra code to be written and have the object be destructed before the new one is constructed?


Solution

  • By definition, if you assign a new value to an old object, the new value has been constructed before the assignment can take place.

    Your 'old object' is not really destructed, either.

    So No. There is no way. And there shouldn't: you shouldn't redefine the 'obvious' behavior of the assignment operator.

    But placement new could help here apart from the tilde and exotic construction syntax, maybe this code approaches 'clean' :)

    Foo old(a, b, c);
    old.~Foo(); // explicit destruction
    new (&old) Foo(d, e, f);