Ошибка нестандартного распределителя в контейнере STL - PullRequest
0 голосов
/ 04 января 2019

Я создал собственный распределитель, который выделяет память при создании и освобождает ее при уничтожении.(чтобы разрешить быстрое распределение / освобождение).Когда я использую его с контейнером STL, все работает отлично!Ожидается, когда я использую метод assign ... Я не понимаю, почему ...

Я пытался напечатать каждый указатель, выделенный / свободный, но все выглядит хорошо.

#include <cstddef>
#include <type_traits>
#include <stack>
#include <numeric>
#include <list>

template <class T>
class CustomAllocator
{
    public:
        using value_type = T;
        using size_type = std::size_t;
        using difference_type = std::ptrdiff_t;
        using propagate_on_container_copy_assignment = std::false_type;
        using propagate_on_container_move_assignment = std::false_type;
        using propagate_on_container_swap = std::false_type;
        using is_always_equal = std::false_type;

        CustomAllocator();
        CustomAllocator(size_type size);
        ~CustomAllocator();

        CustomAllocator(const CustomAllocator&);
        CustomAllocator& operator=(const CustomAllocator&) = delete;
        CustomAllocator(CustomAllocator&& src)
            : m_data(std::move(src.m_data)) , m_free(std::move(src.m_free))
        {
            src.m_data = nullptr;
        }


        CustomAllocator& operator=(CustomAllocator&&) = delete;

        template <class T2>
        CustomAllocator(const CustomAllocator<T2>&);

        template <class T2>
        bool operator==(const CustomAllocator<T2>&) const noexcept;

        template <class T2>
        bool operator!=(const CustomAllocator<T2>&) const noexcept;

        value_type* allocate(size_type);
        void deallocate(value_type* ptr, size_type);

    private:
        template <class>
        friend class CustomAllocator;

        void* m_data = nullptr;
        std::stack<void*> m_free;
};

template <class T>
CustomAllocator<T>::CustomAllocator() : CustomAllocator(1024)
{
}

template <class T>
CustomAllocator<T>::CustomAllocator(size_type size)
{
    m_data = ::operator new(sizeof(T) * size);

    for (auto ptr = static_cast<T*>(m_data) + (size - 1); ptr >= 
         static_cast<T*>(m_data); ptr--)
        m_free.push(ptr);
}

template <class T>
CustomAllocator<T>::CustomAllocator(const CustomAllocator&)
    : CustomAllocator(1024)
{
}

template <class T>
template <class T2>
CustomAllocator<T>::CustomAllocator(const CustomAllocator<T2>&)
    : CustomAllocator(1024)
{
}

template <class T>
CustomAllocator<T>::~CustomAllocator()
{
    if (m_data)
        ::operator delete(m_data);
}

template <class T>
template <class T2>
inline bool CustomAllocator<T>::
operator==(const CustomAllocator<T2>&) const noexcept
{
    return typeid(T) == typeid(T2);
}

template <class T>
template <class T2>
inline bool CustomAllocator<T>::
operator!=(const CustomAllocator<T2>&) const noexcept
{
     return typeid(T) != typeid(T2);
}

template <class T>
typename CustomAllocator<T>::value_type*
CustomAllocator<T>::allocate(size_type size)
{
    if (m_free.empty() || size != 1)
        throw std::bad_alloc();

    auto ptr = m_free.top();

    m_free.pop();

    return reinterpret_cast<value_type*>(ptr);
}

template <class T>
void CustomAllocator<T>::deallocate(value_type* ptr, size_type)
{
    m_free.push(ptr);
}

int main()
{
    std::list<size_t, CustomAllocator<size_t>> containerA;
    std::list<size_t, CustomAllocator<size_t>> containerB;


    for (size_t i = 0; i < 10; ++i)
    {
        for (size_t j = 0; j < 100; ++j)
            containerA.emplace_front();

        // dont works with this
        containerB.assign(10, i);

        containerA.clear();
        containerB.clear();
    }

    return 0;
}

На самом деле сбой программы.Если я прокомментирую 'containerB.assign (10, i);', программа работает.Если я заменю «containerB.assign (10, i);»по программе containerB.emplace_front (); 'программа работает.Если я заменю «containerB.assign (10, i);»по 'containerB.insert (containerB.begin (), 10, i);' - сбой программы.Я не понимаю, почему ...

Ошибка clang и gcc: free (): поврежденные несортированные чанки отменяются (ядро выгружено)

Ошибка с gdb: free (): поврежденные несортированные чанки

Программа получила сигнал SIGABRT, Прервано.__GI_raise (sig = sig @ entry = 6) по адресу ../sysdeps/unix/sysv/linux/raise.c:51 51 ../sysdeps/unix/sysv/linux/raise.c: такого файла или каталога нет.

ОБНОВЛЕНИЕ:

При улучшенном операторе == у меня теперь SIGSEGV: программа получила сигнал SIGSEGV, ошибка сегментации.0x000055555555537a в std :: __ cxx11 :: _ List_base> :: _ M_clear (this = 0x7fffffffdcd0) в /usr/include/c++/8/bits/list.tcc:74 74 __cur = __tmp -> _ M_next;

Выход Valgrind:

==17407== Memcheck, a memory error detector
==17407== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==17407== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==17407== Command: ./a.out
==17407== 
==17407== Invalid read of size 8
==17407==    at 0x10937A: std::__cxx11::_List_base<unsigned long, CustomAllocator<unsigned long> >::_M_clear() (list.tcc:74)
==17407==    by 0x109287: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::clear() (stl_list.h:1507)
==17407==    by 0x108F0C: main (bug.cpp:154)
==17407==  Address 0x5b93b00 is 0 bytes inside a block of size 24,576 free'd
==17407==    at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17407==    by 0x1091CE: CustomAllocator<std::_List_node<unsigned long> >::~CustomAllocator() (bug.cpp:100)
==17407==    by 0x109107: std::__cxx11::_List_base<unsigned long, CustomAllocator<unsigned long> >::_List_impl::~_List_impl() (stl_list.h:382)
==17407==    by 0x109205: std::__cxx11::_List_base<unsigned long, CustomAllocator<unsigned long> >::~_List_base() (stl_list.h:506)
==17407==    by 0x10915B: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::~list() (stl_list.h:834)
==17407==    by 0x109B66: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::insert(std::_List_const_iterator<unsigned long>, unsigned long, unsigned long const&) (list.tcc:122)
==17407==    by 0x109586: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::_M_fill_assign(unsigned long, unsigned long const&) (list.tcc:300)
==17407==    by 0x10926C: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::assign(unsigned long, unsigned long const&) (stl_list.h:897)
==17407==    by 0x108EEE: main (bug.cpp:151)
==17407==  Block was alloc'd at
==17407==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17407==    by 0x109665: CustomAllocator<std::_List_node<unsigned long> >::CustomAllocator(unsigned long) (bug.cpp:76)
==17407==    by 0x10A3B6: CustomAllocator<std::_List_node<unsigned long> >::CustomAllocator<unsigned long>(CustomAllocator<unsigned long> const&) (bug.cpp:92)
==17407==    by 0x109FFE: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::list(unsigned long, unsigned long const&, CustomAllocator<unsigned long> const&) (stl_list.h:717)
==17407==    by 0x109B0B: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::insert(std::_List_const_iterator<unsigned long>, unsigned long, unsigned long const&) (list.tcc:122)
==17407==    by 0x109586: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::_M_fill_assign(unsigned long, unsigned long const&) (list.tcc:300)
==17407==    by 0x10926C: std::__cxx11::list<unsigned long, CustomAllocator<unsigned long> >::assign(unsigned long, unsigned long const&) (stl_list.h:897)
==17407==    by 0x108EEE: main (bug.cpp:151)
==17407== 
==17407== 
==17407== HEAP SUMMARY:
==17407==     in use at exit: 0 bytes in 0 blocks
==17407==   total heap usage: 504 allocs, 504 frees, 668,800 bytes allocated
==17407== 
==17407== All heap blocks were freed -- no leaks are possible
==17407== 
==17407== For counts of detected and suppressed errors, rerun with: -v
==17407== ERROR SUMMARY: 100 errors from 1 contexts (suppressed: 0 from 0)

Ответы [ 3 ]

0 голосов
/ 04 января 2019

Распределители начиная с C ++ 11 могут иметь состояние.

Но распределители копируются из одного и того же состояния общего ресурса распределителя, так что что-то, выделенное исходным распределителем, должно освобождать штраф с копией.

Также оператор == и оператор! = Должны сравнивать состояние, распределители не должны быть равными, если состояние отличается.

Это может быть реализовано с помощью указателя на потенциально общий пул в вашем распределителе, а не с непосредственным наличием пула в распределителе. (Пул общего распределителя иногда называют «ареной»).

До C ++ 11 распределители вообще не могли иметь состояния. Таким образом, объекты могут быть распределены / освобождены даже с помощью созданного по умолчанию распределителя, который каждый раз может быть другим экземпляром. В этом случае ваши операторы равенства верны, но вам нужен один глобальный пул, а не возможность отдельного пула для каждого контейнера.

0 голосов
/ 11 января 2019

После этого обсуждения я реализовал Statelull Alloator, но у меня есть проблема с std :: list.Я отправил вопрос об этом: Контейнер STL не поддерживает распределитель состояний?

0 голосов
/ 04 января 2019

m_data - указатель на void. Существует указатель арифметики, выполненный на m_data в конструкторе CustomAllocator в следующей строке.

for (auto ptr = m_data + (sizeof(T) * (size - 1)); ptr >= m_data; ptr -= sizeof(T))

В соответствии со стандартом, арифметика указателей на void* является некорректной. Это может привести к неопределенному поведению.

EDIT
OP был обновлен, и в void* не выполняется арифметика указателей. Поэтому нам нужно найти другие причины аварии.
Для list контейнер emplace_front не влияет на достоверность итераторов и ссылок. Но и assign, и clear делают недействительными все ссылки, указатели и итераторы, ссылающиеся на элементы контейнера.
Так что программа работает с emplace_front и вылетает с assign.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...