использование std::unique_ptr с распределителями

На этот раз я пробовал свои силы с распределителями и чувствовал, что есть много шансов на утечку ресурсов. Поэтому я подумал, что если бы я использовал std::unique_ptr для их обработки. Я попробовал свои силы с распределителем std::vector. Мой код выглядит так: -

// allocator
#include <iostream>
#include <vector>
#include <memory>
using namespace std;

class X
{
    int x,ID;
    static int i;
    public:
        X()
        {
            cout<<"constructing ";
            ID=++i;
            cout<<"ID="<<ID<<'\n';
        }
        X(int a)
        {
            x=a;
            cout<<"constructing ";
            ID=++i;
            cout<<"ID="<<ID<<'\n';
        }
        void get()
        {
            cout<<"enter x: ";
            cin>>x;
        }
        void disp()
        {
            cout<<"x="<<x<<'\t';
        }
        ~X()
        {
            cout<<"destroying ID="<<ID<<'\n';
        }
};
int X:: i=0;

int main()
{
    ios::sync_with_stdio(false);
    vector<X> v;
    auto alloc = v.get_allocator();
    unsigned int i=0;

    X *p(alloc.allocate(5));        
    for (i=0;i<5;++i)
    alloc.construct (&p[i], i+1);
    unique_ptr<X[]> ptr(p);

    cout<<"\nthe elements are:-\n";
    for (i=0;i<5;++i)
    {
        ptr[i].disp();
        cout << '\t' << (long long)alloc.address(ptr[i]) << '\n';
    }
    cout<<"\n";

    /*for (i=0;i<5;++i)
    alloc.destroy(&p[i]);
    deallocate(p,16)*/

    return 0;
}

К сожалению, этот код вылетает, показывая UB. И что же мне делать ? Как мне манипулировать своим кодом, чтобы он подходил для std::unique_ptr?


person Ankit Acharya    schedule 21.11.2015    source источник
comment
я думаю, вам нужно написать отдельные make_unique type function и custom deleter в этом случае   -  person CppNITR    schedule 21.11.2015
comment
@CppNITR, не могли бы вы объяснить это с помощью кода?   -  person Ankit Acharya    schedule 21.11.2015
comment
см. здесь.   -  person Kerrek SB    schedule 21.11.2015
comment
Пожалуйста, отформатируйте код правильно.   -  person edmz    schedule 21.11.2015
comment
@черный, что ты хочешь, чтобы я сделал   -  person Ankit Acharya    schedule 21.11.2015
comment
@KerrekSB не может быть проще   -  person Ankit Acharya    schedule 21.11.2015
comment
Эй, ты тот, кто начал это, сказав слово на букву А. Вы получили то, что заслужили :-)   -  person Kerrek SB    schedule 21.11.2015
comment
@KerrekSB: P еще ....   -  person Ankit Acharya    schedule 21.11.2015


Ответы (1)


template<typename T>
std::unique_ptr<T[], std::function<void(T *)>> make_T(X *ptr, std::allocator<T> alloc, std::size_t size) {
    auto deleter = [](T *p, std::allocator<T> alloc, std::size_t size) {
        for (int i = 0; i < size; ++i) {
            alloc.destroy(&p[i]);
        }
        alloc.deallocate(p, sizeof(T) * size);
    };

    return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}



int main(int argc, const char * argv[]) {
    std::allocator<X> alloc = std::allocator<X>();

    X *p = alloc.allocate(5);
    for (int i = 0; i < 5; ++i) {
        alloc.construct(&p[i], i + 1);
    }

    auto ptr = make_T(p, alloc, 5);

    return 0;
}

Также можно написать один для создания объектов для вас:

template<typename T, typename... Args>
std::unique_ptr<T[], std::function<void(T *)>> make_T_Construct(std::allocator<T> alloc, std::size_t size, Args... args) {

    X *ptr = alloc.allocate(size);

    for (std::size_t i = 0; i < size; ++i) {
        alloc.construct(&ptr[i], std::forward<Args>(args)...);
    }


    auto deleter = [](T *p, std::allocator<T> alloc, std::size_t size) {
        for (std::size_t i = 0; i < size; ++i) {
            alloc.destroy(&p[i]);
        }
        alloc.deallocate(p, sizeof(T) * size);
    };

    return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}

int main(int argc, const char * argv[]) {
    std::allocator<X> alloc = std::allocator<X>();

    auto ptr = make_T_Construct(alloc, 5, 100);

    return 0;
}

Изменить: чтобы делать то, что вы хотите (отслеживание выделения памяти), вы должны самостоятельно отслеживать выделение памяти с помощью специального распределителя.

template<typename T>
struct Allocator
{
    typedef T value_type;

    Allocator() noexcept {};

    template<typename U>
    Allocator(const Allocator<U>& other) throw() {};

    T* allocate(std::size_t n, const void* hint = 0)
    {
        T* memory = static_cast<T*>(::operator new(n * (sizeof(T) + sizeof(bool))));

        for (std::size_t i = 0; i < n * (sizeof(T) + sizeof(bool)); ++i)
        {
            *reinterpret_cast<bool*>(reinterpret_cast<char*>(memory) + sizeof(bool)) = false;
        }

        return memory;
    }

    void deallocate(T* ptr, std::size_t n)
    {
        ::operator delete(ptr);
    }

    void construct(T* p, const T& arg)
    {
        destroy(p);
        new(p) T(arg);
        *reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = true;
    }

    template<class U, class... Args>
    void construct(U* p, Args&&... args)
    {
        destroy(p);
        ::new(p) U(std::forward<Args>(args)...);

        *reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = true;
    }

    void destroy(T* p)
    {
        if (*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool))) {
            p->~T();
            *reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = false;
        }
    }

    template<class U>
    void destroy(U* p)
    {
        if (*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool))) {
            p->~U();
            *reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = false;
        }
    }
};

template <typename T, typename U>
inline bool operator == (const Allocator<T>&, const Allocator<U>&)
{
    return true;
}

template <typename T, typename U>
inline bool operator != (const Allocator<T>& a, const Allocator<U>& b)
{
    return !(a == b);
}
person Brandon    schedule 21.11.2015
comment
я ценю ваши усилия, но это не может предотвратить утечку памяти - person Ankit Acharya; 21.11.2015
comment
@AnkitAcharya, было бы лучше, если бы вы показали свой код, который просочился - person Anwesha; 21.11.2015
comment
cpp.sh/5wp6b проверьте это. ID = 6 просочится сюда. Я знаю, что он протекает из-за оператора цикла for, но я хочу сказать, что unique_ptr должен позаботиться о такой эрогенной конструкции, как всегда. - person Ankit Acharya; 21.11.2015
comment
Подождите... значит, вы выделяете класс в какую-то память. Перезапись памяти и ожидание автоматического уничтожения класса. Я не знаю, понимаете ли вы, как работают распределители, но класс НИКОГДА не разрушится, пока вы не вызовете его уничтожение. Это не std::unique_ptr вина. Это ваша вина, что вы умышленно слили это. Распределители используют placement new, и вы должны удалить его, явно вызвав деструктор (что и является точкой уничтожения). Твои проблемы в другом. - person Brandon; 21.11.2015
comment
понял @Брэндон, тогда это означает, что unique_ptr явно бесполезен, когда дело доходит до allocators или нет? - person Ankit Acharya; 21.11.2015
comment
@AnkitAcharya это не так.. cpp.sh/5c6u Просто используйте специальный распределитель, который отслеживает выделение памяти.. std::unique_ptr выполняет свою работу правильно. Он уничтожает ваш массив, а не выделенные объекты в этом массиве. Он НЕ знает, что вы разместили эти объекты с помощью нового размещения. Однако, если вы создали распределитель, который ЗНАЕТ эту информацию, то std::unique_ptr будет делать то, что вы хотите (на самом деле это распределитель, который выполняет работу, поскольку он выполняет распределение и уничтожение). - person Brandon; 21.11.2015
comment
@Брэндон, я думаю, тебе следует предпочесть using typedef и noexcept throw() - person CppNITR; 22.11.2015