为什么我的 UniqPtr 对象的大小是 std::unique_ptr 的两倍?

Why my UniqPtr objects are double in size compared to std::unique_ptr?

在实际应用中,我应该坚持使用标准库设施,为了练习和理解这些设施的工作原理,我应该尝试实现自己的设施。

这里我实现了一个智能指针的模拟unique_ptr:

#include<iostream>
#include <memory>


template <typename T>
class DefDel
{
public:
    template <typename U>
    void operator()(U* p)const
    {
        std::cout << "freeing memory...\n";
        delete p;
    }
};

template <typename T>
class DefDel<T[]>
{
public:
    template <typename U>
    void operator()(U* p)const
    {
        std::cout << "freeing memory of an array of objects...\n";
        delete[] p;
    }
};

template <typename T, typename D = DefDel<T>>
class UniqPtr final
{
public:
    UniqPtr(T* = nullptr, D = DefDel<T>{});
    UniqPtr(UniqPtr const&) = delete;
    UniqPtr(UniqPtr&&) noexcept;
    UniqPtr& operator =(UniqPtr const&) = delete;
    UniqPtr& operator =(UniqPtr&&) noexcept;
    ~UniqPtr();
    T& operator*();
    T const& operator*() const;
    T* operator->();
    T const* operator->() const;
    operator bool() const;

private:
    T* ptr_{nullptr};
    D del_{};
};

template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
    ptr_(p),
    del_(del)
{}

template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
    ptr_(std::move(rhs.ptr_)),
    del_(std::move(rhs.del_))
{
    rhs.ptr_ = nullptr;
}

template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
    if(this != &rhs)
    {
        ptr_ = std::move(rhs.ptr_);
        del_ = std::move(rhs.del_);
        rhs.ptr_ = nullptr;
    }
    return *this;
}

template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
    del_(ptr_);
}

template <typename T, typename D>
T& UniqPtr<T, D>::operator*()
{
    return *ptr_;
}

template <typename T, typename D>
T const& UniqPtr<T, D>::operator*() const
{
    return *ptr_;
}

template <typename T, typename D>
T* UniqPtr<T, D>::operator->()
{
    return ptr_;
}

template <typename T, typename D>
T const* UniqPtr<T, D>::operator->() const
{
    return ptr_;
}

template <typename T, typename D>
UniqPtr<T, D>::operator bool() const
{
    return ptr_;
}

// for array
template <typename T, typename D>
class UniqPtr<T[], D> final
{
public:
    UniqPtr(T* = nullptr, D = DefDel<T[]>{});
    UniqPtr(UniqPtr const&) = delete;
    UniqPtr(UniqPtr&&) noexcept;
    UniqPtr& operator =(UniqPtr const&) = delete;
    UniqPtr& operator =(UniqPtr&&) noexcept;
    ~UniqPtr();
    T& operator*();
    T const& operator*() const;
    T* operator->();
    T const* operator->() const;
    operator bool() const;

private:
    T* ptr_{nullptr};
    D del_{};
};

template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(T* p, D del) :
    ptr_(p),
    del_(del)
{}

template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(UniqPtr&& rhs) noexcept :
    ptr_(std::move(rhs.ptr_)),
    del_(std::move(rhs.del_))
{
    rhs.ptr_ = nullptr;
}

template <typename T, typename D>
UniqPtr<T[], D>& UniqPtr<T[], D>::operator = (UniqPtr&& rhs) noexcept
{
    if(this != &rhs)
    {
        ptr_ = std::move(rhs.ptr_);
        del_ = std::move(rhs.del_);
        rhs.ptr_ = nullptr;
    }
    return *this;
}

template <typename T, typename D>
UniqPtr<T[], D>::~UniqPtr()
{
    del_(ptr_);
}

template <typename T, typename D>
T& UniqPtr<T[], D>::operator*()
{
    return *ptr_;
}

template <typename T, typename D>
T const& UniqPtr<T[], D>::operator*() const
{
    return *ptr_;
}

template <typename T, typename D>
T* UniqPtr<T[], D>::operator->()
{
    return ptr_;
}

template <typename T, typename D>
T const* UniqPtr<T[], D>::operator->() const
{
    return ptr_;
}

template <typename T, typename D>
UniqPtr<T[], D>::operator bool() const
{
    return ptr_;
}


int main()
{

    UniqPtr<int[]> upi(new int[3]{57});
    std::cout << sizeof(upi) << '\n';
    std::unique_ptr<int[], DefDel<int[]>> upi2(new int[3]{57});
    std::cout << sizeof(upi2) << '\n';
}

Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)? Is that because of my class is storing a Del_ object as a member?

是的。 因为D del_需要有存储空间,每个T* ptr_都得对齐

If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?

您可以私下派生自 D,而不是让它成为成员。然后一个空 class 的实例化可能会使它不占用额外的存储空间。

template <typename T, typename D = DefDel<T>>
class UniqPtr final : D
{
public:
    UniqPtr(T* = nullptr, D = {});
    UniqPtr(UniqPtr const&) = delete;
    UniqPtr(UniqPtr&&) noexcept;
    UniqPtr& operator =(UniqPtr const&) = delete;
    UniqPtr& operator =(UniqPtr&&) noexcept;
    ~UniqPtr();
    T& operator*();
    T const& operator*() const;
    T* operator->();
    T const* operator->() const;
    operator bool() const;

private:
    T* ptr_{nullptr};
};

template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
    D(del),
    ptr_(p)
{}

template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
    D(std::move(*rhs)),
    ptr_(std::exchange(rhs.ptr_, nullptr))
{}

template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
    using std::swap;
    swap(static_cast<D&>(*this), static_cast<D&>(rhs));
    swap(ptr_, rhs.ptr_);
}

template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
    static_cast<D&>(*this)(ptr_);
}