dimanche 3 juillet 2016

calling destructor in custom container

I am designing a custom C++ template container like this:

template <class C>
class Container {
public:
    ...
    void clear() {
        // should I call destructor on elements here?
        for (i...)
            array[i].~C(); // ?
    }

    ~Container() {
        delete [] array_;
    }
private:
    C* array_;
};

Should I call the destructor on elements manually inside clear()? If I don't, they will be called later when the container is destroyed (since I delete [] array_ in destructor), but this is not the expected behaviour (we expect them to be destroyed right inside clear(), just like std::vector does). However, if I do call the destructors, that memory remains in place and when I will be adding new elements on top of the old ones (which now are destroyed), the assignment operator will be called on those destroyed elements, and this may result in undefined behavior. Suppose I have a class:

class Foo {
public:
    Foo() { foo_ = new int; }
    ~Foo() { delete foo_; } // note I don't explicitly set foo_ to nullptr here
    Foo(Foo &&f) {
        f.foo_ = std::exchange(foo_, f.foo_); // standard practice in move constructors
    }
};

OK, this may look good so far, but now if I make a

Container<Foo> c;
c.add(Foo());
c.clear();
c.add(Foo());

when calling clear() the destructor of the initial Foo is called, leaving its foo_ pointer dangling. Next, when adding the second Foo, the temporary R-value is exchanged with the old contents of the destructed object and when the temp will be destroyed, its destructor will try to delete the same dangling pointer again, which will crash.

So, how to correctly clear() a container without leaving room for double deletion? I also read that its recommended not to set pointers to nullptr in destructor in order not to not hide potential dangling pointer problems.

I'm a little confused about how to approach this.

Aucun commentaire:

Enregistrer un commentaire