Массив как частный член класса

Я пытаюсь создать класс, который имеет частный член, который является массивом. Я не знаю размер массива и не буду, пока значение не будет передано в конструктор. Каков наилучший способ определить конструктор класса, а также определение в файле .h, чтобы разрешить этот размер переменной массива?

Ответы

Ответ 1

Если вам нужен "реальный" массив C-стиля, вам нужно добавить частный элемент-указатель к вашему классу и динамически распределить память для него в конструкторе (с новым). Очевидно, вы не должны забывать освободить его в деструкторе.

class YourClass
{
  private:
    int * array;
    size_t size;

    // Private copy constructor operator to block copying of the object, see later
    // C++03:
    YourClass(const YourClass &); // no definition
    // C++11:
    YourClass(const YourClass&) = delete;

  public:
    YourClass(size_t Size) : array(new int[Size]), size(Size)
    {
        // do extra init stuff here
    };

    ~YourClass()
    {
        delete [] array;
    }
};

Чтобы упростить эту работу, вы можете использовать интеллектуальный указатель (например, boost::scoped_array в С++ 03 или plain std::unique_ptr в С++ 11), которую вы можете инициализировать с помощью списка инициализаторов перед конструктором или просто в конструкторе.

class YourClass
{
  private:
    boost::scoped_array<int> array; // or in C++11 std::unique_ptr<int[]> array;
    size_t size;
  public:
    YourClass(size_t Size) : array(new int[Size]), size(Size)
    {
        // do extra init stuff here
    }

    // No need for a destructor, the scoped_array does the magic
};

Оба этих решения производят объекты, не подлежащие копированию (вы не указали, должны ли они быть скопируемыми и их семантикой копирования); если класс не нужно копировать (что происходит чаще всего), оба они в порядке, и компилятор будет генерировать ошибку, если вы попытаетесь скопировать/назначить один класс другому, в первом случае, поскольку копия по умолчанию конструктор был перегружен частным (или просто удален в С++ 11), во втором случае, потому что boost::scoped_array и std::unique_ptr являются не подлежащими копированию.

Если вместо этого вы хотите иметь объекты для копирования, то вы должны решить, хотите ли вы создать копию, которая разделяет массив (так, просто копию указателя) или если вы хотите создать новый отдельный блок для другой объект.

В первом случае вы должны быть очень осторожны, прежде чем освобождать выделенную память, поскольку другие объекты могут ее использовать; опорным счетчиком является наиболее распространенное решение. Вам может помочь в этом boost:: shared_array (или std::shared_ptr в С++ 11), который автоматически выполняет всю работу отслеживания.

Если вместо этого вы хотите сделать "глубокую копию", вам придется выделить новую память и скопировать все объекты исходного массива в целевой массив. Это не совсем тривиально, чтобы делать правильно, и обычно выполняется с помощью "идиомы копирования и подкачки" .

Тем не менее, самым простым решением является использование std::vector как частного члена: он будет обрабатывать все материалы распределения/дезактивации сам по себе, правильно строя/уничтожая себя, когда объект вашего класса будет построен/разрушен. Более того, он реализует семантику глубокой копии из коробки. Если вам нужно, чтобы ваши абоненты получили доступ к вектору только для чтения, вы можете написать получателя, который возвращает ссылку const_iterator или const на объект vector.

Ответ 2

Использование std::vector - лучший вариант. Если вам когда-либо понадобится передать его функции, которая ожидает указатель на массив (например, GSL часто делает), вы все равно можете пройти &vec[0]...