Возможно ли иметь статическую переменную-член в моем шаблонном классе без необходимости знать об этом пользователя класса? - PullRequest
7 голосов
/ 17 июня 2011

У меня есть шаблонный контейнерный класс, что-то вроде этого игрушечного кода:

template <class ItemType> class MyVector
{
public:
   MyVector() : _numItems(0), _items(NULL) {/* empty */}

   /** Returns a reference to the first item in our array,
     * or a default-constructed item if the array is empty.
     */
   const ItemType & GetFirstItemWithDefault() const
   {
      return (_numItems > 0) ? _items[0] : _defaultItem;
   }

   [other methods omitted because they aren't relevant]

private:
   int _numItems;       // how many valid items (_items) points to
   ItemType * _items;   // demand-allocated
   const ItemType _defaultItem;
};

Этот класс действительно удобен в использовании - любой код может просто #include "MyVector.h", а затем начать объявлять объектытипа MyVector и MyVector и т. д., и все это просто работает (tm) без всякого суеты.

Одна вещь, которая меня беспокоит, это наличие переменной-члена _defaultItem, котораяздесь только для того, чтобы дать GetFirstItemWithDefault () возможность вернуть действительную ссылку, когда контейнер пуст.Возражение заключается в том, что если я объявлю N объектов MyVector, это означает, что N копий _defaultItem также будут присутствовать в ОЗУ - даже если они все идентичны и доступны только для чтения, и поэтому на самом деле должен быть только один из них впроцесс, а не один на MyVector.

Итак, очевидное решение состоит в том, чтобы сделать _defaultItem статическим .... но AFAICT, что сопряжено с затратами: если я это сделаю, это больше невозможно для любого старого кускакод, чтобы просто #include "MyVector.h" и перейти ... теперь пользователь должен обязательно объявить хранилище для этой статической переменной в одном из своих файлов .cpp, что является (а) болью в заднице и (b) означает, что пользователь кода должен быть осведомлен о деталях внутренней реализации класса.Поскольку _defaultItem является закрытой переменной-членом, пользователю класса не нужно думать об этом или даже осознавать, что он существует, не говоря уже о том, что ему нужно объявить хранилище для него.(и что, если два отдельных куска кода оба объявляют хранилище для этого, каждый не зная, что другой сделал то же самое? Разве это не вызвало бы ошибку компоновщика с дублированным символом?): есть ли способ сказать C ++ автоматически предоставлять одно уникальное хранилище (для каждого экземпляра MyVector) для этой статической переменной-члена, чтобы пользователям MyVector не приходилось об этом знать?(Обратите внимание, что он должен быть автоматическим для всех возможных экземпляров MyVector <...>, а не только для нескольких распространенных случаев)

Ответы [ 3 ]

8 голосов
/ 17 июня 2011

Почему бы не сделать этот элемент по умолчанию статическим локальным для функции?

const ItemType & GetFirstItemWithDefault() const
{
    static const ItemType _default;
    return (_numItems > 0) ? _items[0] : _default;
}

Это может быть не то, что вам нужно, если вы хотите снова проверить элемент по умолчанию в какой-то другой функции, но для этого вы можете просто поместить его в отдельную функцию, а также (которая может быть сама по себе статической):

static const ItemType& GetDefault() const
{
  static const ItemType _default;
  return _default;
}

И вызывайте эту функцию, когда вам нужен доступ к элементу по умолчанию.


Тем не менее, я думаю, что иметь элемент по умолчанию не очень хорошо. std::vector также не имеет и не нуждается в этом. Просто скажите пользователю, чтобы проверить, если вектор empty, и все готово. Одна проблема со скрытой статикой в ​​том, что вы не знаете ItemType. Это может быть класс, который ест кучу ресурсов, и вы только что сделали еще один пример этого! Возможно, переосмыслите дизайн этого класса и после этого переключитесь на std::vector. :)

3 голосов
/ 17 июня 2011

Итак, очевидное решение состоит в том, чтобы сделать _defaultItem static .... но AFAICT, который идет со стоимостью: если я сделаю это, это больше невозможно для любого старого кусок кода просто #include «MyArray.h» и вперед ... теперь у пользователя есть чтобы быть уверенным, чтобы объявить хранение для этого статическая переменная в одном из его .cpp файлы, которые (а) боль

Нет . Этот страх не вызывает беспокойства. В template s вы можете (должны) объявить переменную static в том же файле.

template <class ItemType> class MyVector
{
  const ItemType _defaultItem;
};
template <class ItemType>
const ItemType MyVector<ItemType>::_defaultItem; // ok (no multiple symbols)

Также обратите внимание, что этот элемент static будет создан, только если вызывается GetFirstItemWithDefault(). Так что не беспокойтесь о избыточном распределении.

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

3 голосов
/ 17 июня 2011

Если это шаблон, компилятор сделает волшебство за вас. Просто поместите статический член в заголовок, и компилятор позаботится о том, чтобы он только что был создан один раз.

template <class ItemType> 
class MyVector
{
public:
    //...
private:
    static const ItemType _defaultItem;
}; 


template <class ItemType> 
const ItemType MyVector<ItemType>::_defaultItem;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...