Как я могу написать собственное средство удаления для массива, управляемого unique_ptr?

Я пытаюсь найти способ написать собственное средство удаления для массива C, управляемого unique_ptr в шаблонном классе C++. Я целенаправленно пытаюсь сделать утечку памяти класса, заставляя средство удаления ничего не делать. В полном классе некоторые конструкторы выделяют память, а некоторые нет — вместо этого они используют память в буфере необработанных байтов, поступающем из потока данных.

Вот что я пробовал:

template <class T> class Matrix
{
    private:
    int _size;
    std::unique_ptr<T[]> _array;

    public:
    Matrix(int size, void* data) : _size(size)
                                 , _array(NULL, [](T[]* p){})
    {
        _array.reset((T*)data);
    }
};

Код не компилируется, сообщение об ошибке:

In file included from /tmp/test/test/test.cpp:9:
/tmp/test/test/Matrix.h:22:55: error: expected ')'
                                 , _array(NULL, [](T[]* p){})
                                                      ^
/tmp/test/test/Matrix.h:22:51: note: to match this '('
                                 , _array(NULL, [](T[]* p){})
                                                  ^
1 error generated.

person simford    schedule 27.09.2019    source источник
comment
Код не компилируется. Какая у вас ошибка? Не могли бы вы отредактировать свой вопрос и скопировать туда всю ошибку?   -  person    schedule 27.09.2019
comment
Зачем вам иметь unique_ptr, если вы не хотите удалять массив? Не могли бы вы просто использовать необработанный указатель?   -  person AchimGuetlein    schedule 27.09.2019
comment
В полном классе некоторые конструкторы выделяют память, а некоторые нет — вместо этого они используют память в буфере необработанных байтов, поступающем из потока данных. Я хочу, чтобы память была освобождена только в том случае, если конструктор выделил эту память. Использование необработанного указателя и флага для разветвления в деструкторе было бы проще, но у меня есть эта проблема в больших классах, и некоторые API не могут быть изменены.   -  person simford    schedule 27.09.2019
comment
Чтобы предоставить настраиваемое средство удаления, вам нужно использовать второй аргумент шаблона. std::unique_ptr<T[], Deleter>, где Deleter — это тип функции (или объекта), выполняющей удаление. Затем конструкция unique_ptr должна передать второй аргумент этого типа.   -  person Peter    schedule 27.09.2019


Ответы (2)


Прежде всего, всегда создавайте простую настройку для проверки:

int main() {
    using T = int;

    std::unique_ptr<T[]> _array(NULL, [](T[]* p){});

    return 0;
}

Итак, теперь к вашей проблеме:

  • T[]* p недействителен и должен быть T* p.
  • Лямбда, которую вы передаете как средство удаления, не соответствует std::default_delete<T>, которое используется в качестве средства удаления по умолчанию. Итак, вы должны написать std::unique_ptr<T[],std::function<void(T*)>.
  • И NULL может быть реализован как целочисленный тип, поэтому вместо него следует использовать nullptr, иначе gcc не скомпилирует ваш код (поскольку в c++11 вы, как правило, должны использовать nullptr вместо NULL).

Итак, сложив все вместе, вы получите:

int main() {
    using T = int;

    std::unique_ptr<T[],std::function<void(T[])>> _array(nullptr, [](T* p){});

    return 0;
}
person t.niese    schedule 27.09.2019

Средство удаления является параметром шаблона std::unique_ptr. По умолчанию это std::default_delete, но вы можете указать что-то другое, например.

template <typename T>
struct NonDeleter
{
    void operator()(T*) { /* nothing */ }
};

template <typename T>
struct NonDeleter<T[]> : NonDeleter<T> {};

template <class T> class Matrix
{
    private:
    int _size;
    std::unique_ptr<T[], NonDeleter<T[]>> _array;

    public:
    Matrix(int size, void* data) : _size(size)
                                 , _array(static_cast<T*>(data))
    {
    }
};
person Caleth    schedule 27.09.2019