Есть ли смысл для реализации функций копирования-оператор присваивания в классе со всеми константные данные-члены?
Представьте, что у меня есть класс, используемый для представления некоторых тривиальных числовых данных, таких как вектор. Я хочу, чтобы этот класс был неизменяемым с точки зрения его членов, но все еще поддерживал правильное поведение копирования-назначения.
Вот как может выглядеть оболочка этого класса:
class Vector {
public:
Vector(float _x, float _y);
private:
const float x;
const float y;
};
Я хотел бы иметь возможность присвоить новое значение векторному члену в пределах (например) класса. Экранная сущность с позицией, возможно, представленная с помощью неконстантного члена типа Vector
.
Vector
, используемый для хранения ее положения, должен измениться. Вместо того чтобы изменять этот элемент напрямую, я бы предпочел заменить его совершенно новым экземпляром Vector
, инициализированным измененными значениями.
В таком языке, как C#, я бы просто вычислил новые значения для X и Y на основе текущего экземпляра Vector
и назначил новый векторный объект, инициализированный этими значениями член, отбрасывая Предыдущее значение и позволяя GC сделать остальное.
// Inside some class (C#/Java type)...
Vector position;
...
// Example function:
void Move(float _dX, float _dY) {
this.position = new Vector(_dX + position.X, _dY + position.Y);
}
Именно здесь в игру вступает моя ограниченная способность С++, потому что я не уверен, как реализовать operator=
таким образом, чтобы экземпляры этого неизменяемого типа, содержащиеся в динамической памяти, не просто "исчезали" и не вызывали утечку.
Итак, я полагаю, что спрашиваю вот о чем:
Vector& operator=(const Vector& _rhs);
...
// In implementation file...
Vector& Vector::operator=(const Vector& _rhs) {
// WHAT ON EARTH GOES IN HERE?!?!?
}
Случай Vector
- это то, что я придумал, чтобы донести свою точку зрения, я могу думать о многих типах, которые должен быть неизменным на уровне членов.
Не трачу ли я впустую свое время, пытаясь смоделировать неизменяемые типы?
Есть ли лучшее решение?
6 ответов:
Когда вы набираете
Vector position;
В определении класса вы определяете экземпляр класса
Vector
, который останется здесь навсегда. Если он неизменен, то ты облажался.В C# / Java нет способа сделать это. Вместо этого вы определяете ссылки.
Если вы хотите сделать то же самое в C++, вам нужны указатели или auto_pointers
Таким образом, вы можете изменить экземпляр, на который указываетVector* position;
position
.Не следует определять оператор присваивания a неизменяемый класс. Если вам действительно нужно, возможно, вам следует сделать
Vector
изменяемым и использоватьconst Vector
, Когда вам нужен неизменяемыйVector
. Вам просто нужно будет добавитьconst
квалификаторы к методу, которые разрешены с помощьюconst Vector
.Например:
class Vector { private: float x; float y; public: // allowed for const Vector // because of the const before the '{' float norm() const { return hypot(x,y); } Vector& operator=(const Vector& o) { x=o.x; y=o.y; return *this; } };
То, что вы пытаетесь сделать, не имеет смысла для использования с оператором=. Причина в том, что вам нужен экземпляр вашего векторного класса, чтобы использовать оператор=, но вы заявили, что не хотите разрешать изменения внутренних элементов.
Обычно ваш оператор= хотел бы что-то вроде:
Vector::operator=(const Vector &rhs) { x = rhs.x; y = rhs.y; }
* Обратите внимание, что компилятор автоматически сгенерирует это для вас, и это будет безопасно, поскольку у вас нет указателей и т. д.
Этот оператор = перегрузка не будет работать для вашего сценария, b/c члены данных x и y не будут переназначать b/c, которые у вас есть как const. Здесь вы будете использовать конструктор копирования со списком инициализации. Если бы вы действительно хотели иметь operator=, вы могли бы использовать const_cast внутри перегрузки, но большинство посчитало бы эту форму плохой.
Vector::Vector(const Vector ©) :x(copy.x), y(copy.y) { }
* Примечание компилятор также автоматически создаст этот тип конструктора копирования для вас.
Тогда ваш код создаст объект с помощью
Vector newObj(oldObj);
Изложив все это, чтобы ответить на ваш вопрос: почему бы просто не позволить членам быть неконстантными и не объявить вектор const?
const Vector myVector(5,10);
У вас есть логическое несоответствие в ваших требованиях. С одной стороны, все объекты класса Vector должны быть неизменяемыми, но с другой стороны вы хотите иметь возможность изменять их. Эти два требования находятся в прямом противоречии друг с другом.
Лучше было бы сделать классы такими, что единственный способ изменить значение-это назначить ему новый объект класса:
class Vector { public: Vector(float _x, float _y); /* Vector(const Vector&) = default; */ /* Vector& operator=(const Vector&) = default; */ float x() const; float y() const; /* No non-const accessors!! */ private: float x; float y; };
После инициализации экземпляры этого класса не могут быть изменены, кроме как полностью перезаписываю их новым экземпляром.
Если вас беспокоит правило трех здесь, сделайте класс
noncopyable
или объявите оператор присваивания копии какprivate
(и оставьте его неопределенным).Если это не подходит для вашего типа данных, то это означает, что эти элементы данных не должны быть
const
.
Я думаю, что вы несколько неправильно понимаете const правильность. Если вы действительно хотите иметь неизменяемую структуру, то оператор= не имеет смысла.
Однако я не вижу, почему приведенный ниже код не соответствует вашим потребностям:
struct Vector { Vector(float, float); Vector(Vector const&); private: const float x, y; // Disable automatic generation of operator= Vector& operator=(Vector const&); };