Имеет ли смысл реализовывать оператор присваивания при копировании в классе со всеми элементами данных const? - PullRequest
2 голосов
/ 07 сентября 2010

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

Вот как может выглядеть оболочка этого класса:

class Vector {
  public:
  Vector(float _x, float _y);

  private:
  const float x;
  const float y;
};

Я хотел бы иметь возможность назначить новое значение члену вектора внутри (например) класса. Экранная сущность с позицией, возможно, представленной с использованием неконстантного члена типа Vector.

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

На таком языке, как C #, я просто вычислял бы новые значения для X и Y на основе текущего экземпляра Vector и назначал бы новый объект 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);
}

Именно здесь вступают в игру мои ограниченные возможности в C ++, потому что я не уверен, как реализовать operator= таким образом, чтобы экземпляры этого неизменяемого типа, содержащиеся в динамической памяти, не просто «исчезали» и вызывали утечку .

Итак, я полагаю, вот что я спрашиваю:

Vector& operator=(const Vector& _rhs);
...
// In implementation file...
Vector& Vector::operator=(const Vector& _rhs) {
  // WHAT ON EARTH GOES IN HERE?!?!?
}

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

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

Есть ли лучшее решение?

Ответы [ 6 ]

3 голосов
/ 07 сентября 2010

При вводе

Vector position;

в определении класса вы определяете экземпляр класса Vector, который останется здесь навсегда. Если тот неизменен, вы облажались.

В C # / Java нет способа сделать это. Вместо этого вы определяете ссылки.

Если вы хотите сделать то же самое в C ++, вам нужны указатели или auto_pointers

Vector* position;

Таким образом, вы можете изменить экземпляр, на который указывает position.

Вы не должны определять оператор присваивания неизменяемого класса. Если вам действительно нужно, возможно, вы должны сделать 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;
    }
};
2 голосов
/ 07 сентября 2010

У вас есть логическое несоответствие в ваших требованиях. С одной стороны, все объекты класса 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;
};

После инициализации экземпляры этого класса не могут быть изменены, за исключением случаев полной перезаписи их новым экземпляром.

2 голосов
/ 07 сентября 2010

То, что вы пытаетесь сделать, не имеет смысла для использования с оператором =. Причина в том, что вам нужен экземпляр вашего класса Vector для использования оператора =, но вы заявили, что не хотите разрешать изменения внутренних компонентов.

Обычно ваш оператор = хотел бы что-то вроде:

Vector::operator=(const Vector &rhs)
{
   x = rhs.x;
   y = rhs.y;
}

* Обратите внимание, что компилятор автоматически сгенерирует это для вас, и это будет безопасно, поскольку у вас нет указателей и т. Д.

Этот оператор = перегрузка не будет работать для вашего сценария, т.к. элементы данных x и y не будут переназначаться, поскольку они у вас как const. Здесь вы бы использовали конструктор копирования со списком инициализации. Если вы действительно хотите иметь оператор =, вы можете использовать const_cast внутри перегрузки, но большинство будет рассматривать эту плохую форму.

Vector::Vector(const Vector &copy)
   :x(copy.x), y(copy.y)
{

}

* Обратите внимание, что компилятор также автоматически сгенерирует этот тип конструктора копирования для вас.

Тогда ваш код создаст объект с

Vector newObj(oldObj);

Сказав все это, чтобы ответить на ваш вопрос, почему бы просто не позволить членам быть неконстантными и объявить Вектор константным?

const Vector myVector(5,10);
1 голос
/ 07 сентября 2010

Если вас беспокоит правило трех здесь, создайте класс noncopyable или объявите оператор присваивания копии как private (и оставьте его неопределенным).

Если это не подходит для вашего типа данных, это означает, что эти элементы данных не должны быть const.

0 голосов
/ 07 февраля 2012

Это должно сделать это.

Vector& Vector::operator=(const Vector& _rhs) {
    Vector tmp( _rhs.x, _rhs.y );
    std::swap( *this, tmp );
    return *this;
}
0 голосов
/ 07 сентября 2010

Я думаю, что вы несколько неправильно понимаете правильность.Если вы действительно хотите неизменную структуру, иметь оператор = не имеет смысла.

Однако я не понимаю, почему приведенный ниже код не соответствует вашим потребностям:

struct Vector
{
    Vector(float, float);
    Vector(Vector const&);

private:
    const float x, y;

    // Disable automatic generation of operator=
    Vector& operator=(Vector const&);
};
...