Възможно ли е да имам статична членска променлива в моя шаблонен клас, без потребителят на класа да знае за това?

Имам шаблонен клас контейнер, нещо като този код на играчка:

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;
};

Този клас е наистина удобен за използване -- всеки код може просто да #включи "MyVector.h" и след това да започне да декларира обекти от тип MyVector и MyVector и така нататък, и всичко просто работи (tm) без да е необходимо каквото и да е бъркане.

Едно нещо, което ме притеснява обаче, е наличието на променливата член _defaultItem, която е там единствено, за да даде възможност на GetFirstItemWithDefault() да върне валидна препратка, когато контейнерът е празен. Възражението е, че ако декларирам N MyVector обекта, това означава, че N копия на _defaultItem ще присъстват и в RAM --- въпреки че всички те са идентични и само за четене, така че наистина трябва да има само едно от тях на процес, а не един на MyVector.

И така, очевидното решение е да направя _defaultItem статичен .... но AFAICT, който идва с цена: ако направя това, вече не е възможно нито една стара част от кода просто да #include "MyVector.h" и да тръгне. .. сега потребителят трябва да е сигурен, че е декларирал съхранение за тази статична променлива в един от своите .cpp файлове, което е (а) болка в задника и (б) означава, че потребителят на кода трябва да е наясно от детайлите на вътрешната реализация на класа. Тъй като _defaultItem е частна членска променлива, потребителят на класа не трябва да мисли за нея или дори да осъзнава, че съществува, камо ли да знае, че трябва да декларира място за съхранение за нея. (и какво ще стане, ако два отделни фрагмента код декларират съхранение за него, като всеки не знае, че другият е направил едно и също нещо? Това няма ли да причини грешка на свързващия дублиран символ?)

Следователно въпросът ми е: има ли някакъв начин да кажа на C++ автоматично да предоставя едно уникално хранилище (за инстанциран тип на MyVector) за тази статична променлива член, така че потребителите на MyVector да не знаят за това? (Имайте предвид, че трябва да е автоматично за всички възможни инстанции на MyVector‹...>, а не само за няколко често срещани случая)


person Jeremy Friesner    schedule 17.06.2011    source източник


Отговори (3)


Ако е шаблон, компилаторът ще направи магията вместо вас. Просто поставете статичния член в заглавката и компилаторът ще се погрижи той да бъде създаден само веднъж.

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


template <class ItemType> 
const ItemType MyVector<ItemType>::_defaultItem;
person Bo Persson    schedule 17.06.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. :)

person Xeo    schedule 17.06.2011
comment
Просто, елегантно, подходящо от начина, по който прочетох въпроса. +1 - person 0xC0000022L; 17.06.2011
comment
Този модел има приложение. Ако повечето обаждащи се трябва да проверяват за празни, но биха могли еднакво да функционират само с по подразбиране, тогава общият код е опростен чрез формуляр, който връща по подразбиране. - person edA-qa mort-ora-y; 17.06.2011
comment
@edA-qa mort-ora-y: вярно, може значително да опрости определен код, но трябва ли да е членска функция? Оправдава ли нов клас Array? Бихте могли напр. създайте обвиващ обект Defaulter, конструиран с препратка към std::vector или използвайки композиция, която замества обекта по подразбиране според нуждите. - person Tony Delroy; 17.06.2011
comment

Разреших го. Намерих го на този уебсайт http://forums.adobe.com/thread/761395

Опитвах се да накарам trace() да работи в Mac. Компилирах приложението си AIR с помощта на компилатора на командния ред на flex „amxmlc“ и включих параметри -debug=false и -omit-trace-statements=false. След това стартирах приложението с adl your-app.xml -nodebug и успях да вкарам следите в терминала.

- person David Rodríguez - dribeas; 17.06.2011

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

Не. Този страх е от притеснение. В templates можете (трябва) да декларирате променливата 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 ще разчита общо на конструктора по подразбиране (за който вече трябва да се интересувате, предполагам). Това е.

person iammilind    schedule 17.06.2011