Учитывая класс как это:
class Foo
{
const int a;
};
Действительно ли возможно поместить тот класс в вектор? Когда я пробую, мой компилятор говорит мне, что не может использовать оператор присваивания по умолчанию. Я пытаюсь записать, что мое собственное, но гуглящий вокруг говорит мне, что невозможно записать оператор присваивания для класса с элементами данных константы. В одном сообщении, которое я нашел, было сказано, что, "при создании [элемента данных] константой, которая означает, Вы не хотите, чтобы присвоение произошло во-первых". Это имеет смысл. Я записал класс с элементами данных константы, и я никогда не предназначал при использовании присвоения на нем, но по-видимому мне нужно присвоение для помещения его в вектор. Существует ли путь вокруг этого, которое все еще сохраняет правильность константы?
Я написал класс с константными членами-данными, и я никогда не намеревался использовать для него присваивание, но, очевидно, мне нужно присваивание, чтобы поместить его в вектор. Есть ли способ обойти это, сохраняя константную корректность?
Вы должны спросить, выполняется ли по-прежнему следующее ограничение
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;
}
};
Но подумайте дважды! . Мне кажется, что ваш тип не удовлетворяет ограничению!
Используйте вектор указателей std::vector
. Если вы хотите избежать хлопот по уборке после себя, используйте boost::p tr_vector
.
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
, вероятно, плохая идея с точки зрения дизайна.) На практике, выбирая между ними, я использую члены только для чтения.Это, вероятно, означает, что вы сможете заменить значение объекта значением другого объекта, не нарушая семантику вообще. Давайте посмотрим, как это будет работать в вашем случае.
Рассмотрим объект Grid с шириной и высотой. Когда вы изначально создаете вектор, и, скажем, вы резервируете некоторое начальное пространство, используя vector::reserve()
, ваш вектор будет заполнен начальными инициализированными по умолчанию (т.е. пустыми) сетками. Когда вы переходите к назначению определенной позиции в векторе или нажимаете сетку на конец вектора, вы заменяете значение объекта в этой позиции сеткой, которая имеет фактические данные. Но вы можете быть в порядке с этим! Если причина, по которой вы хотели, чтобы ширина и высота были постоянными, на самом деле заключается в том, чтобы обеспечить согласованность между шириной и высотой и остальным содержимым объекта Grid, и вы убедились, что не имеет значения, заменяются ли ширина и высота до или после замены других элементов Grid,тогда это назначение должно быть безопасным, так как к концу назначения все содержимое экземпляра будет заменено, и вы вернетесь в согласованное состояние. (Если отсутствие атомарности назначения по умолчанию было проблемой, вы, вероятно, могли бы обойти это, реализовав свой собственный оператор присваивания, который использовал конструктор копирования и операцию swap()
.)
Таким образом, с помощью геттеров только для чтения вы получаете возможность использовать объекты в векторе или любом контейнере с семантикой значений. Однако затем вам выпадает убедиться, что ни одна из внутренних операций Grid (или операций друзей Grid) не нарушает эту согласованность, потому что компилятор не будет блокировать ширину и высоту для вас. Это касается строительства по умолчанию, построения копий и назначения.
Я подумываю сделать член данных неконстантным, но закрытым и доступным только для функции get, например:
class Foo
{
private:
int a;
public:
int getA() const {return a;}
};
Это «так же хорошо», как и const? Есть ли у него недостатки?