Подтвердить что ты не робот

Как передать deleter в make_shared?

Поскольку С++ 11, по нескольким причинам, разработчики склонны использовать классы интеллектуального указателя для динамических объектов жизни. И с этими новыми классами умных указателей, стандартами даже предлагайте не использовать таких операторов, как new, вместо этого они предлагают использовать make_shared или make_unique, чтобы избежать склонности к ошибкам.

Если нам нравится использовать класс интеллектуальных указателей, например shared_ptr, мы можем построить один, например,

shared_ptr<int> p(new int(12));

Также мы хотели бы передать пользовательский deleter для классов интеллектуального указателя,

shared_ptr<int> p(new int(12), deleter);

С другой стороны, если мы хотим использовать make_shared для распределения, например. int, вместо использования конструктора new и shared_ptr, как и в первом выражении выше, мы можем использовать

auto ip = make_shared<int>(12);

Но что, если нам нравится также передавать пользовательский deleter на make_shared, есть ли правильный способ сделать это? Похоже, что компиляторы, по крайней мере, gcc, дают ошибку,

auto ip = make_shared<int>(12, deleter);
4b9b3361

Ответ 1

Как говорили другие, make_shared не может использоваться с пользовательским делетером. Но я хочу объяснить, почему.

Пользовательские удалители существуют, потому что вы выделили указатель каким-то особым образом, и поэтому вам нужно будет освободить его соответствующим образом. Ну, make_shared выделяет указатель new. Объекты, выделенные с помощью new, должны быть освобождены с помощью delete. Который стандартным делетом покорно делает.

Короче говоря, если вы можете жить с положением распределения по умолчанию, вы также можете жить с поведением дезадаптации по умолчанию. И если вы не можете жить с положением распределения по умолчанию, вы должны использовать allocate_shared, который использует предоставленный распределитель для распределения и освобождения хранения.

Кроме того, make_shared разрешено (и почти наверняка будет) выделять память для T и блок управления для shared_ptr в пределах того же распределения. Это то, о чем ваш неудачник не может действительно знать или иметь дело. В то время как allocate_shared способен обрабатывать его, поскольку предоставленный вами распределитель может выполнять обязанности по распределению и освобождению.

Ответ 2

Как из документации, make_shared принимает список аргументов, с помощью которых будет создан экземпляр T.
Кроме того, в документации указано, что:

Эта функция обычно используется для замены конструкции std:: shared_ptr (новый T (args...)) общего указателя из необработанного указателя, возвращаемого вызовом new.

Из-за этого вы можете сделать вывод о том, что вы не можете установить пользовательский отладчик.
Для этого вам нужно создать shared_ptr для себя с помощью конструктора .
В качестве примера конструктора из предложенного списка вы можете использовать:

template< class Y, class Deleter > 
shared_ptr( Y* ptr, Deleter d );

Таким образом, код будет выглядеть примерно так:

auto ptr = std::shared_ptr(new MyClass{arg1, arg2}, myDeleter);

Вместо:

auto ptr = std::make_shared<MyClass>(arg1, arg2);

Ответ 3

Вы не можете. make_shared<T> пересылает предоставленные аргументы конструктору типа T. Он используется для простого случая, когда вы хотите деблокировать по умолчанию.

Ответ 4

Неизвестно, как make_shared получает память для объекта (он может использовать operator new или malloc или какой-то распределитель), поэтому пользовательский удаленный пользователь не может знать, как поступать правильно. make_shared создает объект, поэтому вам также нужно полагаться на него, чтобы правильно уничтожить объект и выполнить соответствующую очистку, что бы это ни было.

Также мы хотели бы передать пользовательский deleter для классов интеллектуального указателя,

shared_ptr<int> p(new int(12), deleter);

Я не думаю, что это очень реалистичный пример. Пользовательский отладчик обычно используется, когда ресурс был получен каким-то особым образом. Если вы только что создали его с помощью new, то почему вам нужен пользовательский удалён?

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

struct RunSomethingOnDestruction {
  RunSomethingOnDestruction(int n) : i(n) { }
  ~RunSomethingOnDestruction() { /* something */ }
  int i;
};

auto px = std::make_shared<RunSomethingOnDestruction>(12);
std:shared_ptr<int> p(px, px->i);

Это дает вам shared_ptr<int>, созданный с помощью make_shared (так что вы получите оптимизацию памяти, сделанное с помощью make_shared), который запустит какой-то пользовательский код при уничтожении.

Ответ 5

Если вы используете пользовательское средство удаления, вы не можете использовать функции make_unique или make_shared при создании объектов умного указателя. Так как мы должны предоставить наше собственное средство удаления, эти функции не поддерживают это.

Не используйте make_unique или make_shared, если вам нужно пользовательское удаление или использование необработанного указателя из другого места.

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

Пусть скажем мы класс Test

#include <iostream>    
using namespace std;    
class Test
{
private : 
    int data; 
    public : 
    Test() :data{0}
    {
        cout << "Test constructor (" << data << ")" << endl;
    }
    Test(int d) : data{ d }
    {
        cout << "Test constructor (" << data << ")" << endl; 
    }
    int get_data() const { return data; }
    ~Test()
    { 
        cout << "Test Destructor (" << data << ')' << endl; 
    }
};

// main function. 
int main()
{
   // It fine if you use  make_shared and custom deleter like this
   std::shared_ptr<Test> ptr(new Test{1000},
            [](Test *ptr)
            {
                cout << "some Code that you want to execute "; 
                delete ptr;
            });
         return 0;
}

Но если вы используете функцию make_shared, вы получите ошибку компилятора

std::shared_ptr<Test> ptr = make_shared<Test>(1000,
            [](Test *ptr){
               cout << "some Code that you want to execute "; 
               delete ptr;
            });

В основном make_shared функция является оболочкой для new и delete, и если вы хотите пользовательские Deleter вы должны обеспечить вас есть new и delete