У меня есть класс — который в основном представляет собой структуру группы полей (скажем, что-то вроде двумерной точки; но это просто синтетический пример для целей этого вопроса) — из которых я хочу только неизменные экземпляры. Очевидно, я могу сделать члены данных приватными и иметь только методы получения:
class Point2d {
protected:
double x;
double y;
public:
Point2d(double _x, double _y) : x(_x), y(_y) {}
double getX() const { return x; }
double getY() const { return y; }
double norm() const { return x*x + y*y; }
}
Но я подумал, может быть, лучше сделать это:
class Point2d {
public:
const double x;
const double y;
public:
Point2d(double _x, double _y) : x(_x), y(_y) {}
double norm() const { return x*x + y*y; }
Point2d operator=(const Point2d& p) = delete;
}
Редактировать: Третий вариант — обеспечить неизменность, используя только const
в объявлении отдельных переменных, причем сам класс является изменяемым, т.е.
class Point2d {
public:
double x;
double y;
public:
Point2d(double _x, double _y) : x(_x), y(_y) {}
double norm() const { return x*x + y*y; }
}
// ...
Point2d magicalTransformation(const Point2d& p1, const Point2d& p2);
Что является более разумным способом обеспечения неизменности?
При разработке класса всегда существует напряжение между гибкостью и гарантиями. Обеспечение целостности элементов данных, что исключает присвоение, делает жесткий класс: это имеет цену (потеря гибкости), но какова выгода?
Конечно, любой пользователь, который хочет замерзать экземпляр может сделать это с помощью const
, Таким образом, вы не предоставляете пользователю никаких «новых» функций, но лишаете их гибкости: стоит ли это того?
Здесь я бы подумал, что вы слишком экстремальны, так как назначение не будет работать, у вас могут возникнуть трудности с использованием экземпляров класса в наиболее распространенных операциях (вы можете создать их массив, но позже не сможете отсортировать массив).
Таким образом, я бы посоветовал вам просто предотвратить любые изменения Кроме от назначения. Этого достаточно, чтобы легко поддерживать классовые инварианты.
Это считается разумным?
Да. На мой взгляд, это лучший стиль, так как он улучшает читабельность.
Я бы когда-нибудь предпочел const
(неизменный) public
член над подходом геттера. Фактически, каждый тип данных отправляет определенное сообщение, если оно объявлено надлежащим образом. Например:
const
data ==> Член не будет изменен в своемpublic
==> Член читается вне class
protected
member ==> Член читается в наследствеprivate
member ==> Член читается в class
только областьТак что здесь цель ясна. Вы объявляете пользователя const
как только вы уверены, что это не изменится в течение всей жизни. Спецификатор доступа должен выбираться в зависимости от того, где будут использоваться потенциальные методы получения.
Кроме того, мне действительно нужен модификатор const в методе norm с
const public
Члены?
Да. Это предпочтительнее, потому что вам нужно const
правильная версия для const
объекты или внутри другого const
методы.
редактировать:
По поводу измененного вопроса. После того, как у вас есть один const
член внутри class
, operator =
неявно удаляется, поэтому вам не нужно = delete
Это. Это просто вопрос вкуса. Для меня это становится излишним.
На самом деле в g ++ компиляторе, если вы попытаетесь изменить такой объект с помощью присваивания, компилятор сам сгенерирует ошибку, такую как:
error: use of deleted function ‘Point2D& Point2D::operator=(const Point2D&)’
Другое утверждение является объявлением функции:
const Point2d findMagicPoint();
Тип возвращаемого значения не обязательно должен быть const
потому что это все равно значение, которое нельзя изменить. например findMagicPoint() = <something>;
плохо сформирован.
Оба будут работать.
Скорее всего, между ними нет разницы в производительности, так как getX()
а также getY()
оптимизируется до «ничего». [Вы, вероятно, хотите использовать return y;
в GetY
tho ‘, это может помочь устранить ошибку или два].
Это в основном вопрос стиля.
Конечно, вы, вероятно, также будете иметь non-const
версия этого класса, для тех случаев, когда вы хотите сделать вычисления по точке перед ее использованием.
Я не уверен, может ли быть предоставлен факт для этого вопроса, но, по моему мнению, использование get и set для таких небольших объектов, как Point2D, является излишним. Если вы собираетесь использовать эти объекты в качестве констант, просто сделайте их постоянными. Если вы собираетесь использовать их как переменные, просто перегрузите операторы, такие как «+», «+ =» и т. Д., И используйте их. Point2D как элемент данных должен быть инкапсулирован, а не элементы данных Point2D. Опять же, если вы собираетесь использовать их неизменным образом, просто используйте const Point2D
, Вы можете изменить (использовать изменяемым образом) данные Point2D позже. Постоянные члены данных не служат цели для этого класса.