Как передать 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);
Ответы
Ответ 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