#c #const-cast
#c #const-приведение
Вопрос:
Я прочитал довольно много обсуждений о том, что const_cast в C считается неправильным и опасным и не должен использоваться ни для чего, кроме обратной совместимости с кодом C. И я в целом согласен.
Однако недавно я наткнулся на следующий вариант использования, и это заставило меня задуматься.
У меня есть некоторый объект модели, принадлежащий контроллеру, которыйобычно неконстантен. Я передаю его в представление, которое не должно иметь возможности изменять объект модели, поэтому было бы логично объявить объект в качестве параметра const при передаче в представление. Однако представление также выполняет обратные вызовы делегирования контроллеру, передавая объект модели в качестве параметра (например, пользователь нажал на этот объект). Параметр обратного вызова также должен быть const. В методе обратного вызова контроллер хочет внести изменения в модель, но модель является const, поэтому он не может этого сделать.
Два решения:
- используйте const_cast в методе обратного вызова контроллера, поскольку он знает, что объект модели неконстантен, но он пахнет.
- не объявляйте модель как параметр const при передаче в представление — но тогда я не смогу использовать такой полезный инструмент, как объявление const, которое кажется очень актуальным в данном случае.
Мысли?
Комментарии:
1. Почему контроллер владеет моделью? И если представление не доступно только для чтения, почему вы ожидаете иметь модель const?
2. «Неправильный и опасный» — это неправильно и опасно. Для
const_cast
есть подходящие варианты использования, и игнорировать их из-за какого-то изречения — плохое кодирование.3. @vahancho: потому что контроллер является точкой входа в приложение. Он создает модель и представления и устанавливает связи между ними. Само представление доступно только для чтения, но изменения в модели должны быть внесены контроллером в ответ на события представления.
4. @Пит Беккер: вот почему я спрашиваю в первую очередь. Не хочу не использовать что-то только потому, что кто-то так сказал). Просто любопытно проверить мнения коллег, чтобы понять, не упускаю ли я из виду некоторые вещи и каковы практики других.
Ответ №1:
Есть два способа справиться с const
.
-
Вещи,
const
если этот код не изменит их напрямую. -
Вещи,
const
если этот код их не изменит.
В этом случае представление не будет напрямую изменять объект. Но вы хотите, чтобы это косвенно изменило объект.
В соответствии с (2) это означает, что объект не является const
. Представление может изменить объект, хотя и косвенно. Это может привести к изменению объекта. Утверждение, что это const
подразумевает, что взаимодействие представлений с объектом является чисто «состоянием чтения», а не изменением состояния, но нажатие на кнопку «Удалить» объекта и его удаление — это операция изменения.
В соответствии с (1) ваша ссылка должна быть const
, потому что вы сами ее не изменяете. Кто-то другой, в соответствии с полномочиями, предоставленными их правом делать это.
Это один конфликт. И (1) является приемлемым способом использования const
. Но при использовании (1) у вас должен быть альтернативный маршрут к объекту как не- const
.
Мы можем увидеть это в разделе vector.erase
. Это (сейчас) занимает const_iterator
секунды. Даже если этим итераторам самим не разрешено изменять вектор, отсутствие const
ness of *this
предоставляет альтернативный путь доступа, который разрешает модификацию.
В вашем случае контроллер владеет объектом, поэтому должен иметь не const
путь доступа к этому объекту. Это путь, который вы должны использовать, чтобы не const
изменять объект.
Когда представление выполняет обратный вызов делегата, оно может передавать идентификатор вместо объекта — или контроллер может каким-то образом извлечь идентификатор из объекта и просмотреть его в своем собственном списке объектов.
Комментарии:
1. Спасибо, я думал об этом, но идентификаторы объектов выглядят как большой перебор. Я не хочу замедлять свой код с помощью таблицы подстановки только для того, чтобы обойти некоторые языковые особенности. Передача необработанного указателя объекта в представление фактически направлена на быстрое получение целевой модели обратно в обратном вызове контроллера без дополнительного поиска.
Ответ №2:
Это не очень хороший случай для const_cast
. Если view
принимает model
как const
, то ему может быть предоставлен действительно const
экземпляр model
. Тогда было бы неправильно пытаться передать этот экземпляр контроллеру. Если ссылка на model
, данная view
, может быть использована для изменения model
, то этого не должно быть const
.
Комментарии:
1. Да, но поскольку я владею всей кодовой базой проекта, я могу договориться с самим собой о том, что истинные модели const никогда не должны использоваться 🙂
2. Однако это вводит новое соглашение, которое вы должны помнить. И это один из худших видов условностей, который противоречит ожиданиям любого, кто не знает об этом. Когда функция или конструктор принимает
const
ссылку, вы предполагаете, что передаватьconst
экземпляр безопасно, предположение, которое нарушает ваше соглашение. Это не делает это хорошим аргументом дляconst_cast
, это очень плохое использованиеconst_cast
.3. Но без этого соглашения я должен заключить другое соглашение о том, что я не изменяю объект в коде представления. Из этих двух соглашений я думаю, что модели const едва ли применимы, в то время как изменение модели из представления может быть заманчивым.
4. @user1548418 Но одно из этих соглашений на порядок более разрушительно, если нарушено, чем другое. И вы можете относительно легко обеспечить, чтобы представление не могло изменять не-
const
модель, в то время как очень сложно, чтобы дляconst
ссылки не был предоставленconst
экземпляр. Одним из способов может быть требование базовогоview
класса, который владеет неконстантным указателем и предоставляет толькоconst
средство получения для производныхview
классов. Наконец, никто не будет сомневаться, что им лгут оconst
и вряд ли будут искать это, в то время как они могут искать ограничения при реализацииview
класса.5. Мой объект модели — это дерево, поэтому нет простого способа написать такой геттер для узла дерева. Вот почему я в первую очередь передаю узел модели в параметре обратного вызова. И, как я сказал выше, я не хочу вводить таблицу поиска узлов только для того, чтобы обойти языковую особенность. Представление взаимодействует только с контроллером — оно получает и отправляет данные модели только контроллеру. Таким образом, соглашение о неконстантных моделях может быть инкапсулировано в контроллере, а не во всем проекте. Для чего подходит модель const в реальных приложениях, чтобы я беспокоился об этом?