1

When I am writing a demo string class, in the copy assignment function, I try to clear itself through 'delete this' before copy.But it failed.

    Str &operator=(const Str &s) {
        if (this != &s) {  // self-assignment check
            //delete this; //cannot run as I imagine
            this->~Str();  
            _size = s._size;
            _str = new char[_size + 1];
            memcpy(_str, s._str, _size + 1);
        }
        return *this;
    }
    ~Str() {
        _size = 0;
        delete[] _str;
    }

the linux told me

double free or corruption (out) Aborted (core dumped)

ln vv
  • 13
  • 3
  • `delete this` calls `this->~Obj();` and `free(this)`. One deletes objects via `delete` when they were created via `new`. While `this->~Obj();` manually calls destructor, to be used with placement new. – ALX23z Feb 13 '23 at 12:52
  • `delete this` runs the destructor *and* frees whatever memory your object was given. Just write a separate "dealloc" function and call that instead of playing tricks with the destructor. – Botje Feb 13 '23 at 12:53
  • [Is "delete this" allowed in C++?](https://stackoverflow.com/questions/3150942/is-delete-this-allowed-in-c) is worth a read. – Retired Ninja Feb 13 '23 at 12:53
  • 1
    @ALX23z Not `free(this);`, but `operator delete(this);`. – HolyBlackCat Feb 13 '23 at 12:54
  • Please also read: [What is The Rule of Three?](/questions/4172722) – Karl Knechtel Feb 13 '23 at 12:56

2 Answers2

7

delete x; is equivalent to x->~T(); followed by operator delete(x) (which is similar to free(x), but might be incompatible with it).

x->~T(); is a dangerous tool. In this case it must be followed by new(this) T(...); (placement-new) to call a constructor, otherwise the current object is considered to be "dead", and any interaction with it causes undefined behavior.

But even if you do call placement-new, it can throw. Then the object remains dead, and the caller gets UB when it tries to destroy the already dead object again.

Conclusion: x->~T(); is hard to use correctly, use something else.

  • Either write a function that does the same thing as the destrutor, and call it instead. Unlike the destructor, the object wouldn't be considered dead after calling it, so no placement-new is needed.

  • Or use the copy&swap idiom. Write the assignment as:

    Str &operator=(Str s) noexcept
    {
        std::swap(_str, s._str);
        std::swap(_size, s._size);
        return *this;
    }
    

    This is an universal way of writing assignments. It works both as copy and move assignment, is exception-safe, etc.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 1
    the most sensible use of `x->~T();` is if you created the object with placement-new, I think. – user253751 Feb 13 '23 at 13:20
  • 1
    @user253751 Mhm, I only meant that it's unsuitable for this usecase. – HolyBlackCat Feb 13 '23 at 14:18
  • 1
    Another trap with `x->~T()` followed by placement new is that if the original type of the object that `x` points at was a type `U` that's derived from `T` the result will be a non-viable hybrid that's neither a `T` nor a `U`. – Pete Becker Feb 13 '23 at 16:10
1

Your delete this already executes this->~Str() but that's only one problem. After delete this you call _size = s._size which is actually this->_size = s._size. But lifetime of this already ended so any call to this can lead to undefined behavior. So not delete this nor this->~Str() belong to your assign operator.

user10
  • 266
  • 5