Средства доступа без ссылок () или const на переменную-член - PullRequest
4 голосов
/ 08 марта 2011

Я заинтересован в создании класса, который я могу использовать как

class MyClass {
  vector<int> m_vec;
public:
  // Either this
  const& vector<int> vec;
  // Or some version of this. 
  const& vector<int> getVec() { return m_vec } ;

  MyClass() : vec(m_vec) {}
  void changeVec() { m_vec.push_back(5); }
}

Теперь, если я хочу использовать getVec (), синтаксис немного громоздок:

myClass.getVec()[5]

IЯ бы предпочел иметь возможность каким-либо образом использовать

myClass.vec[5]

без предоставления возможности изменять вектор.То есть, я хочу, чтобы переменная-член была закрытой, но const-версия переменной была бы общедоступной без синтаксических или служебных издержек.

Если я добавлю ссылку const & vector, компилятор (по крайней мереверсия GCC) фактически заставляет класс занимать больше памяти.Итак,

  1. Как создать псевдоним для переменной-члена, не заставляя ее использовать больше памяти, или
  2. Как избежать паренов в аксессоре myVec () []?Просто определение myVec (int idx) не является удовлетворительным вариантом, так как я хочу вызвать несколько методов для него.

Просто чтобы прояснить, это пример - в реальном случае использование немного более убедительно, чем в случае вектора.Кроме того, использование памяти важно, так как многие из этих объектов существуют и будут скопированы, так что это не является преждевременной оптимизацией.На данный момент я использую подход getVec () [0], но уродство убивает меня.

Ответы [ 6 ]

5 голосов
/ 08 марта 2011

Если по какой-то причине MyClass::operator[] не подходит, просто создайте крошечный класс-обертку для вашего вектора.

struct Wrapper {
  friend class MyClass;
  int operator[](size_t s) const { return vec[s]; }
private:
  vector<int> vec;
};

class MyClass {
public:
  Wrapper vec;
};
4 голосов
/ 08 марта 2011

Вы уверены, что пытаетесь решить правильную проблему здесь?Одной из важных целей классов является недопущение раскрытия базовой реализации пользователям, и в этом случае вы делаете именно такую ​​экспозицию.

Вам действительно следует внимательно изучить пользовательский API вашего класса.* Какие операции действительно необходимо выполнить с контейнерами?Вы должны разработать свой API таким образом, чтобы методы описывали процессы высокого уровня класса, а НЕ с точки зрения «получить мне скрытые подробности реализации».

Если вам просто нужно предоставить доступ кэлементы различных контейнеров, которые вы можете предоставить начальные / конечные итераторы и разрешить алгоритмы, которые работают на диапазонах.В качестве альтернативы вы можете предоставить функции, такие как for_each_boys и for_each_girls, которые вызывают операнд для соответствующей функции.

3 голосов
/ 08 марта 2011

В качестве рекомендации вы можете перегрузить operator[] для MyClass:

int operator[] (size_t which) const {
   return m_vec[which]; 
}

Это значительно упрощает синтаксис для доступа к вектору: myClass[5].Тем не менее, это становится непрактичным, если вы а) хотите выставить более одного вектора вместе с вашим классом или б) намереваетесь использовать другие std::vector члены, чем просто подписку массива, т.е. size() ... в этом случае вы не можете реальноизбегайте добытчика и, следовательно, скобок.

1 голос
/ 08 марта 2011

Перво-наперво: я не вижу никакого смысла делать это вообще. Теперь, если вы намереваетесь избежать синтаксиса ()[], на котором я настаиваю: я бы не стал, вы можете просто предложить ссылку на тип.

class test {
   std::vector<int> m_data;
public:
   std::vector<int> &data;
   test() : m_data(), data( m_data ) {}
};

Преимущество перед упаковщиками заключается в том, что его проще кодировать, и с ним не так уж много проблем. Недостатком является то, что вы пропускаете детали своей реализации: внешний код будет зависеть от реализации вашего объекта в терминах std::vector, для которого вы предлагаете ссылку. Обратите внимание, что с упаковщиком нет большой разницы в этом отношении, так как в обоих случаях вы предлагаете доступ к деталям ( Я предложу вам индексированный доступ к моим внутренним элементам ), которые могут быть или не быть хорошая идея.

Другие простые в коде, не очень хорошие решения могут подразумевать использование наследования, как в:

class test : private std::vector<int>
{
public:
   using std::vector<int>::operator[];
};

Важной деталью здесь является то, что вы никогда не должны публично наследовать от контейнера STL (на самом деле, будучи class, ключевое слово private выше необязательно, так как наследование по умолчанию закрытый, но я добавил его для акцента), они не были предназначены для полиморфной работы. Используя частное наследование, вы ограничиваете риск ошибок пользователя, поскольку этот вектор является деталью реализации.

Если позднее вы решите изменить вектор для другого контейнера (который допускает индексированный доступ - опять же, вы обещаете индексированный доступ в интерфейсе!), Вам нужно только предоставить свои собственные реализации operator[] и пользователя код будет скомпилирован без изменений.

1 голос
/ 08 марта 2011

Самое простое:

struct MyClass
{
    vector<int> const vec;

    MyClass()
    {
        vector<int>& mvec = const_cast<vector<int>&>(vec);
        // modify mvec
    }
};
0 голосов
/ 08 марта 2011

Вы могли бы сделать это: (Не то чтобы я думаю, что это была бы особенно хорошая идея ...)

class MyClass;

class Proxy{
    public:
    Proxy(MyClass& c); // store reference to c
    int const& operator[](...) const; // access c.vec

    private:
    MyClass& c;
};

class MyClass{
    public:
    // public read-only proxy for this->m_vec
    const Proxy vec;

    MyClass()
        : vec(*this){
    }
    ...
    private:
    vector<int> m_vec;
};

Не проверено, но я уверен, что вы поняли идею.Этот тип шаблона полезен для предоставления таких вещей, как кусочки массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...