为什么我的 UniquePtr 实现会双重释放?

wee*_*eeb 2 c++ memory

当我运行这个程序时,我为 unique ptr 的实现获得了双重释放。知道为什么会发生这种情况吗?

#include <iostream>
#include <memory>
using namespace std;

template <class T>
class UniquePtr
{
public:
        UniquePtr(T* t = nullptr) : t_(t) {}
        UniquePtr(const UniquePtr&) = delete;
        UniquePtr& operator=(const UniquePtr& oth) = delete;
        UniquePtr(UniquePtr&& oth) {
                std::swap(t_, oth.t_);
        }
        UniquePtr& operator=(UniquePtr&& oth) {
                std::swap(t_, oth.t_);
                return *this;
        };
        ~UniquePtr() {
                delete t_;
        }

private:
        T* t_;
};

struct Obj {
        Obj(int x): x_(x) { cout << "new " << x_ << endl; }
        ~Obj() { cout << "delete " << x_ << endl; }
        int x_;
};


template <class UP>
void test(UP&&) {
        {
                UP ptr(new Obj(1));
        }
        {
                UP ptr(UP(new Obj(2)));
        }
        {
                auto lambda = []() {
                        UP ptr(new Obj(3));
                        return ptr;
                };
                UP ptr2(lambda());
        }
}
int main() {
        cout << "unique_ptr" << endl;
        test(unique_ptr<Obj>());
        cout << endl;

        cout << "UniquePtr" << endl;
        test(UniquePtr<Obj>());
        cout << endl;

        return 0;
}
Run Code Online (Sandbox Code Playgroud)
unique_ptr
new 1
delete 1
new 2
delete 2
new 3
delete 3

UniquePtr
new 1
delete 1
new 2
delete 2
new 3
delete 3
delete 0
free(): double free detected in tcache 2
Aborted
Run Code Online (Sandbox Code Playgroud)

Ala*_*les 6

t_在移动构造函数中未初始化,因此移动自指针最终指向未初始化的指针并具有未定义的行为(当移动自对象解构并delete保存未初始化的指针时,这将导致问题)。你需要:

UniquePtr(UniquePtr&& oth)
: t_(nullptr)
{
    std::swap(t_, oth.t_);
}
Run Code Online (Sandbox Code Playgroud)

或者可能更简单:

UniquePtr(UniquePtr&& oth)
: t_(std::exchange(oth.t_, nullptr)
{
}
Run Code Online (Sandbox Code Playgroud)