Используя класс с элементами данных константы в векторе

Учитывая класс как это:

class Foo
{
   const int a;
};

Действительно ли возможно поместить тот класс в вектор? Когда я пробую, мой компилятор говорит мне, что не может использовать оператор присваивания по умолчанию. Я пытаюсь записать, что мое собственное, но гуглящий вокруг говорит мне, что невозможно записать оператор присваивания для класса с элементами данных константы. В одном сообщении, которое я нашел, было сказано, что, "при создании [элемента данных] константой, которая означает, Вы не хотите, чтобы присвоение произошло во-первых". Это имеет смысл. Я записал класс с элементами данных константы, и я никогда не предназначал при использовании присвоения на нем, но по-видимому мне нужно присвоение для помещения его в вектор. Существует ли путь вокруг этого, которое все еще сохраняет правильность константы?

10
задан Max 26 May 2010 в 22:20
поделиться

4 ответа

Я написал класс с константными членами-данными, и я никогда не намеревался использовать для него присваивание, но, очевидно, мне нужно присваивание, чтобы поместить его в вектор. Есть ли способ обойти это, сохраняя константную корректность?

Вы должны спросить, выполняется ли по-прежнему следующее ограничение

a = b;
 /* a is now equivalent to b */

Если это ограничение неверно для a и b ] имеет тип Foo (вы должны определить семантику того, что означает «эквивалент»!), то вы просто не можете поместить Foo в стандартный контейнер. Например, auto_ptr нельзя поместить в стандартные контейнеры, поскольку он нарушает это требование.

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

class Foo
{
   const int a;
public:
   Foo &operator=(Foo const& f) {
     /* don't assign to "a" */
     return *this;
   }
};

Но подумайте дважды! . Мне кажется, что ваш тип не удовлетворяет ограничению!

8
ответ дан 4 December 2019 в 00:23
поделиться

Используйте вектор указателей std::vector. Если вы хотите избежать хлопот по уборке после себя, используйте boost::p tr_vector.

3
ответ дан 4 December 2019 в 00:23
поделиться

Edit: Мой первый удар во время кофе-брейка, static const int a; не будет работать для сценария использования, который имеет в виду OP, что подтверждают первоначальные комментарии, поэтому я переписываю и расширяю свой ответ.

В большинстве случаев, когда я хочу сделать элемент класса константой, это константа, значение которой является постоянным для всех времен и для всех экземпляров класса. В этом случае я использую статическую переменную const:

class Foo
{
public:
    static const int a;
};

Их не нужно копировать между экземплярами, поэтому, если она применяется, это решит проблему назначения. К сожалению, ОП указала, что это не сработает для случая, который имеет в виду ОП.

Если вы хотите создать значение read-only, которое клиенты не могут изменять, вы можете сделать его закрытой переменной-членом и предоставить его только с помощью метода const getter, как указывает другой пост в этом потоке:

class Foo
{
public:
    int get_a() const { return a; }
private:
    int a;
};

Разница между этим и

class Foo
{
public:
    const int a;
};

составляет:

  • const int дает вам уверенность в том, что даже реализация класса не сможет запутаться со значением a в течение всего срока службы объекта. Это означает, что назначение по праву не будет работать, так как это будет попытка изменить значение a после создания объекта. (Вот почему, кстати, написание пользовательского operator=(), который пропускает копию a, вероятно, плохая идея с точки зрения дизайна.)
  • Доступ отличается - вы должны пройти через getter, а не получить доступ к участнику напрямую.

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

Рассмотрим объект Grid с шириной и высотой. Когда вы изначально создаете вектор, и, скажем, вы резервируете некоторое начальное пространство, используя vector::reserve(), ваш вектор будет заполнен начальными инициализированными по умолчанию (т.е. пустыми) сетками. Когда вы переходите к назначению определенной позиции в векторе или нажимаете сетку на конец вектора, вы заменяете значение объекта в этой позиции сеткой, которая имеет фактические данные. Но вы можете быть в порядке с этим! Если причина, по которой вы хотели, чтобы ширина и высота были постоянными, на самом деле заключается в том, чтобы обеспечить согласованность между шириной и высотой и остальным содержимым объекта Grid, и вы убедились, что не имеет значения, заменяются ли ширина и высота до или после замены других элементов Grid,тогда это назначение должно быть безопасным, так как к концу назначения все содержимое экземпляра будет заменено, и вы вернетесь в согласованное состояние. (Если отсутствие атомарности назначения по умолчанию было проблемой, вы, вероятно, могли бы обойти это, реализовав свой собственный оператор присваивания, который использовал конструктор копирования и операцию swap().)

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

1
ответ дан 4 December 2019 в 00:23
поделиться

Я подумываю сделать член данных неконстантным, но закрытым и доступным только для функции get, например:

class Foo
{
    private:
        int a;
    public:
        int getA() const {return a;}
};

Это «так же хорошо», как и const? Есть ли у него недостатки?

0
ответ дан 4 December 2019 в 00:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: