Можно ли создать оператор сравнения "безопасный для будущего" в C ++? - PullRequest
10 голосов
/ 22 декабря 2010

Привет всем,

Можно ли создать оператор сравнения, безопасный для будущего (==) в C ++?

Проблема, с которой я столкнулся, заключалась в том, что у нас есть класс с несколькими членами. У нас есть оператор сравнения для проверки, если экземпляр-1 объекта имеет те же значения, что и экземпляр-2.

т.е. мы можем сделать

class blarg {
    .....
};

.....

blarg   b1(..initializers...);
blarg   b2 = b1;

if (b1 == b2) {
    ... then do something ....
}

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

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

Моей первой реакцией было использование команды memcmp. Однако после прочтения записи о переполнении стека для « Сравнение структур в C против C ++ » я вижу, что это может быть проблематично из-за того, что классы C ++ содержат не только данные-члены внутри.

Как другие справляются с этим?

Заранее благодарю за помощь.

Ответы [ 5 ]

8 голосов
/ 22 декабря 2010

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

Поэтому решение этой проблемы с философской, а не технической точки зрения можетчасто дают понимание.Философия в этом случае заключается в том, чтобы быть параноиком.Предположим, что код, который вы пишете сегодня, будет нарушен через несколько месяцев или лет.(Отредактируйте комментарии @ Ноя ниже: чаще всего этот самозванец - это я сам. Будучи параноиком, я защищаю себя от себя , вероятно, больше, чем кто-либо другой.) Если вы можете что-то сделать, чтобы гарантировать, что когда придурокнарушает ваш код, что-то не получается перед поставкой продукта, это поможет.

Мне нравятся две вещи: статические утверждения и модульные тесты.Статическое утверждение может использоваться в вашем коде operator==, чтобы убедиться, что ваш класс sizeof соответствует вашим ожиданиям.Например:

bool MyClass::operator==(const MyClass& rhs) const
{
  static_assert(sizeof(MyClass) == sizeof(foo_) + sizeof(bar_))
  ...
}

... где foo_ и bar_ - переменные-члены.Это нарушит компиляцию при изменении размера класса.

Поскольку статическое утверждение обычно принимает форму шаблонного класса, который не будет компилироваться, когда выражение ложно.Это может быть немного сложно написать (но это интересное упражнение - подумайте, что произойдет, если вы попытаетесь добавить члена char test_[0] в класс).К счастью, это колесо уже изобретено.См. Boost для примера, и я думаю, что новый компилятор MSVC также поставляется с ним.

7 голосов
/ 22 декабря 2010

ОК, реальный ответ сейчас.

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

2 голосов
/ 13 сентября 2012

Единственный реальный способ решить эту проблему - вообще не иметь оператора присваивания для этого класса.Вы спросите, что это?Что если у меня есть динамическая память и т. Д., Для которой требуется глубокая копия?

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

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

0 голосов
/ 23 декабря 2010

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

  • определяемые пользователем конструкторы (в частности, их списки инициализаторов)
  • деструктор (если ваш класс когда-либо использует члены, которые в этом нуждаются, хотя если деструктору нужноучитывать более одного участника, с которым вы, вероятно, попали)
  • swap
  • operator<<(std::ostream&, const blarg&) или другие формы (де) сериализации
  • operator<
  • operator==
  • [C ++ 0x] hash функция
  • вероятно, что-то еще я забыл.

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

Проще, если все такие функции определены (или, по крайней мере, объявлены) вместе: конечно, я все равно оставляю вместе первые три «жизненных цикла» и никогда не трогаю участника, не просматривая его.Три «сравнения» в идеале также близки друг к другу, поскольку они должны быть взаимно согласованными, поэтому, как только укоренилась привычка, контрольный список может оказаться довольно простым взглядом на класс.«Я изменил структуру класса, поэтому мне нужно посмотреть здесь, чтобы увидеть, какие функции нужно просмотреть, помимо изменений, которые я уже имею в виду ...»

Очевидно, что если вы сможете управлять этим, вы будете следоватьпринцип Open / Closed, и этого никогда не происходит.Лично мне это никогда не удавалось.

0 голосов
/ 22 декабря 2010

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

Для обычных классов это не совсем вариант - я бы порекомендовал провести для этого юнит-тесты, как указал Ноа Робертс в своем ответе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...