2010-04-26 2 views
3

Мне нужен класс типа std :: auto_ptr для массива unsigned char *, выделенного новым []. Но auto_ptr вызывает только delete, а не delete [], поэтому я не могу его использовать.Автоматический указатель для массива unsigned char?

Мне также нужна функция, которая создает и возвращает массив. Я вышел с моей собственной реализацией в классе ArrayDeleter, который я использую, как в этом примере:

#include <Utils/ArrayDeleter.hxx> 

typedef Utils::ArrayDeleter<unsigned char> Bytes; 

void f() 
{ 
    // Create array with new 
    unsigned char* xBytes = new unsigned char[10]; 
    // pass array to constructor of ArrayDeleter and 
    // wrap it into auto_ptr 
    return std::auto_ptr<Bytes>(new Bytes(xBytes)); 
} 

... 
// usage of return value 
{ 
    auto_ptr<Bytes> xBytes(f()); 
}// unsigned char* is destroyed with delete[] in destructor of ArrayDeleter 

Есть ли более элегантный способ решить эту проблему? (Даже используя другую «популярную» библиотеку)

ответ

11

Boost имеет множество авто-указателей, в том числе для массивов. Считаете ли вы, достаточно ли std :: vector? Векторы гарантированно смежны в памяти, и если вы заранее знаете размер и выделенную память, то местоположение в памяти не изменится.

+0

Я посмотрел на повышение, и я думаю, что scoped_array сделает это. Спасибо – Gianluca

+3

@Gianluca: Почему вы не используете вектор? – GManNickG

+0

@GMan: Конечно, теперь я вижу, что это гораздо лучшая идея. Спасибо – Gianluca

2

Как насчет использования std::basic_string<unsigned char>? Или, может быть, std::vector<unsigned char>?

+0

Затем мне нужно вызвать методы, которые принимают unsigned char * в качестве аргумента. Есть ли способ получить доступ к нему из одного из контейнеров, которые вы предлагаете? Будут ли данные метода basic_string() делать это? – Gianluca

+0

Вы можете использовать метод std :: string. .c_str() ', это возвращает char * с нулевым завершением. – Konrad

3

Мне нужно вызвать методы, которые принимают unsigned char * в качестве аргумента.

std::vector<unsigned char> vec; 
. 
. 
. 
legacy_function(&vec[0], vec.size()); 
+0

perfect, thanks – Gianluca

-1
  1. Вы говорите о массиве int, а не сложные C++ типов, которые имеют деструктор. Для такого массива вызов delete[] эквивалентен вызову delete. Поэтому нет проблем с использованием std::auto_ptr.

  2. Метод, который вы предлагаете, очень варварский ИМХО. Вы фактически выделяете память дважды: один раз для необходимого массива, а затем вы также выделяете экземпляр ArrayDeleter, который инкапсулирует указатель на выделенный массив.

К недостаткам такого способа являются:

  • худшую производительность. Операции кучи тяжелые. Кроме того, они имеют значительные накладные расходы памяти.
  • Медленный доступ. Чтобы получить доступ к элементу вашего массива с помощью std::auto_ptr<Bytes>, компилятор будет генерировать две косвенности: один для получения вашего объекта Bytes, а другой - для доступа к элементу. Простыми словами: std::auto_ptr имеет указатель на объект Bytes, в котором указатель на массив.
  • Худшая ошибка/согласованность исключений. Представьте себе, что если operator new не может выделить объект Bytes. Он будет генерировать исключение, которое может быть обработано. Но на данный момент у вас есть уже выделяет массив. И это распределение будет потеряно.

Я хотел бы сделать одно из следующих действий:

  1. Если вы говорите обычного типа - просто используйте std::auto_ptr<type>. Это должно делать работу. Однако вы должны проверить его с вашим компилятором.

  2. Для сложных типов: вы можете создать свою собственную упаковку вместо std::auto_ptr.

  3. Другой вариант: аналогично тому, что вы делали. Однако вы должны избавиться от дополнительных распределений памяти и косвенностей.
+2

Вызов 'delete' в динамически распределенном массиве ВСЕГДА НЕ УКАЗАН ПОВЕДЕНИЕ, даже если он всегда работал на вас на всех компиляторах, которые вы пытались. – fredoverflow

+0

delete [] НЕ эквивалентно вызову delete, даже для POD. См. 18.6.1.2 (Формы массива) в стандарте: «void operator delete [] (void * ptr) throw(); ... Требуется: ptr ... должно быть значением, возвращенным предыдущим вызовом Оператор new [] (std :: size_t) или operator new [] (std :: size_t, const std :: nothrow_t &) " – Francesco

+0

Именно по этой причине я написал« вы должны проверить это с помощью своего компилятора ». Строго говоря, вы правы. В стандарте говорится, что это неопределенное поведение. OTOH Я сказал, что * на самом деле * происходит. По крайней мере, на компиляторе MSVC. Причина, по которой «delete []» следует использовать вместо «удалить», не потому, что нам нужно знать, сколько памяти должно быть освобождено - согласно этому аргументу вам нужно передать размер памяти в «свободную» функцию как Что ж. Причина в том, что когда вы использовали «delete []», компилятор генерирует вызовы для объектов объектов в дополнение к освобождению памяти. – valdo