Можете ли вы использовать shared_ptr для RAII массивов в стиле C?
Я работаю над секцией кода, которая имеет много возможных точек отказа, из-за которых она рано выходит из функции. Библиотеки, с которыми я взаимодействую, требуют, чтобы массивы C-стиля были переданы в функции. Поэтому вместо вызова delete на массивах в каждой точке выхода я делаю это:
void SomeFunction(int arrayLength)
{
shared_ptr<char> raiiArray(new char[arrayLength]);
pArray = raiiArray.get();
if(SomeFunctionThatRequiresCArray(pArray) == FAILED) { return; }
//etc.
}
Я хотел использовать unique_ptr
, но мой текущий компилятор не поддерживает его, и в этом случае накладные расходы ссылочного значения не имеют большого значения.
Мне просто интересно, есть ли у кого-нибудь мысли об этой практике при взаимодействии с устаревшим кодом.
ОБНОВЛЕНИЕ Я полностью забыл о вызове shared_ptr
delete
вместо delete []
. Я просто не видел утечек памяти и решил пойти с ним. Даже не думал использовать вектор. Поскольку я в последнее время вникал в новый (для меня) С++, я думаю, что у меня есть случай "Если у вас единственный инструмент, это молот, все выглядит как гвоздь". синдром. Спасибо за отзывы.
UPDATE2 Я решил, что изменил бы вопрос и предоставил бы ответ, чтобы сделать его немного более ценным для тех, кто совершил ту же ошибку, что и я. Хотя есть альтернативы типа scoped_array
, shared_array
и vector
, вы можете использовать shared_ptr
для управления областью массива (но после этого я понятия не имею, почему я хочу):
template <typename T>
class ArrayDeleter
{
public:
void operator () (T* d) const
{
delete [] d;
}
};
void SomeFunction(int arrayLength)
{
shared_ptr<char> raiiArray(new char[arrayLength], ArrayDeleter<char>());
pArray = raiiArray.get();
if(SomeFunctionThatRequiresCArray(pArray) == FAILED) { return; }
//etc.
}
Ответы
Ответ 1
Не используйте shared_ptr
или scoped_ptr
для хранения указателей на динамически распределенные массивы. shared_ptr и scoped_ptr использовать delete ptr;
для очистки, когда указатель больше не ссылается/выходит за пределы области видимости, которая вызывает поведение undefined в динамически распределенном массиве. Вместо этого используйте shared_array или scoped_array, которые правильно используют delete[] ptr;
при разрушении.
Чтобы ответить на ваш вопрос, если вы не собираетесь использовать интеллектуальный указатель, используйте scoped_array
, так как он имеет меньше накладных расходов, чем shared_array
.
В качестве альтернативы используйте std::vector
в качестве хранилища массивов (векторы имеют гарантированное смежное распределение памяти).
Ответ 2
Используйте boost::scoped_array
или даже лучше std::vector
, если вы имеете дело с массивом.
Ответ 3
Я очень рекомендую просто использовать std::vector
. Элементы в vectors
выделяются в куче и будут удалены, когда vector
выходит за пределы области видимости, где бы вы ни покидали функцию.
Чтобы передать vector
в устаревший код, требующий массивы стиля C, просто пройдите &vectorName[0]
. Элементы гарантированно смежны в памяти.
Ответ 4
Некоторые замечания для пользователей С++ 11:
Для shared_ptr
в С++ 11 используется деблокировка по умолчанию для типов массивов, определенных в <memory>
, и стандартная совместимость (по окончательной черновике), поэтому для таких случаев они могут использоваться без дополнительных причудливых удалений:
std::shared_ptr<char> raiiArray(new char[arrayLength], std::default_delete<char[]>());
unique_ptr
в С++ 11 имеет частичную специализацию для работы с new[]
и delete[]
. К сожалению, у него нет общего поведения. Должна быть хорошей причиной, по которой нет такой специализации для shared_ptr
, но я не искал ее, если вы ее знаете, пожалуйста, поделитесь ею.
Ответ 5
Здесь boost::scoped_ptr
.
Ответ 6
Это
shared_ptr<char*> raiiArray(new char[arrayLength]);
не является хорошей практикой, но вызывает поведение undefined, поскольку вы выделяете оператор new[]
, но shared_ptr
использует operator delete
для освобождения памяти. Правильная вещь - boost::shared_array
или добавить пользовательский отладчик.