Comments 27
С одной стороны, задавать подобные вопросы на собеседовании — дело бесполезное, это почти ничего не говорит о способностях интервьюируемого.Все бы так собеседования проводили :)
+3
Определение mutable в классе зачастую излишне раскрывает внутреннюю жизнь класса. Если вы делаете класс для внешнего API, то изменения его внутренней структуры, той же технологии кеширования, как в статье, заставляет пользователей вашего API перекомпилировать свои программы.
Поэтому мне больше по вкусу скрытая реализация внутренних данных, как это использовано в Qt.
Поэтому мне больше по вкусу скрытая реализация внутренних данных, как это использовано в Qt.
class ObjectPrivate;
class QObject {
private
QObjectPrivate *d;
public:
// ... any const methods
};
+1
Использовать кучу правило или не использовать выбираете вы. Примеров есть кучи, где d на структуру, в контейнере выделенную один раз, указывает.
Не, пусть Йоде будет йоднутое. Короче, вас никто не обязывает выделять на каждый объект отдельную структуру из кучи. В стеке, конечно, выделить так память не получится, но можно заранее отложить память в объекте-контейнере/фабрике под N объектов.
Что да, в очередной…
Не, пусть Йоде будет йоднутое. Короче, вас никто не обязывает выделять на каждый объект отдельную структуру из кучи. В стеке, конечно, выделить так память не получится, но можно заранее отложить память в объекте-контейнере/фабрике под N объектов.
Что да, в очередной…
0
Только если для внешнего API.
В остальных случаях это только утруднит написание и ухудшит производительность кода.
Оба подхода нужны. Я просто считаю что такие решения должны приниматься из практических соображений, а не быть делом вкуса.
В остальных случаях это только утруднит написание и ухудшит производительность кода.
Оба подхода нужны. Я просто считаю что такие решения должны приниматься из практических соображений, а не быть делом вкуса.
0
Поэтому мне больше по вкусу скрытая реализация внутренних данных, как это использовано в Qt.Возможность изменения реализации без перекомпиляции — это же совсем другая проблема, здесь не рассматриваемая.
А решение проблемы спецификаторов
const
в публичном API на QObject
, придётся так или иначе передать классу QObjectPrivate
, и там снова думать — использовать mutable
, или что-то другое. 0
Вы хорошо подумали?
QObjectPrivate, во-первых, не публичный, его структура скрыта, во-вторых, не обязан иметь const методов.
QObjectPrivate, во-первых, не публичный, его структура скрыта, во-вторых, не обязан иметь const методов.
0
Я почему-то подумал, что внутри const-метода нельзя вызывать не-const методы по указателю, который описан в классе:
Но этот пример у меня скомпилировался.
Другое дело, если бы QObjectPrivate был не указателем, а членом класса QObject.
// QObject.h
class QObject {
class QObjectPrivate;
QObjectPrivate *d;
public:
int area() const;
};
// QObject.cpp
class QObject::QObjectPrivate
{
public:
int area() { return 0; }
};
int QObject::area() const
{
return d->area(); // вот здесь вызов не-const метода
}
Но этот пример у меня скомпилировался.
Другое дело, если бы QObjectPrivate был не указателем, а членом класса QObject.
// QObject.h
class QObjectPrivate
{
public:
int area() { return 1; }
};
class QObject {
QObjectPrivate d;
public:
int area() const;
};
// QObject.cpp
int QObject::area() const
{
return d.area(); // вот здесь ошибка
}
Я думал, что поведение должно быть аналогичным. 0
Да, в вызове d->area() значение указателя, лежащее в QObject, не меняется, следовательно для компилятора константность QObject соблюдена.
Еще обратите внимание, что во втором случае, когда QObjectPrivate является членом класса QObject, вам пришлось перенести его определение в .h, т.е. раскрыть детали его реализации пользователям QObject. Что не всегда желательно.
Еще обратите внимание, что во втором случае, когда QObjectPrivate является членом класса QObject, вам пришлось перенести его определение в .h, т.е. раскрыть детали его реализации пользователям QObject. Что не всегда желательно.
+1
Для решения этой проблемы специально придумали std::experiemntal::propagate_const
(ещё не в стандарте)
+2
Не знаю, кем mutable забыт, но мне иногда приходится его использовать.
+3
В стаье незаслуженно забыт старый добрый const_cast. Он кстати позволяет скрыть текущие абстракции (в данном случае текущие конвенции) и перенести их из заголовков в реализацию, где им и место.
+1
А не будет ли это UB?
Если я сниму const с класса через const_cast, и вызову его не-const метод.
Если я сниму const с класса через const_cast, и вызову его не-const метод.
0
Только если у исходного объекта, у которого вызывается метод стоит квалификатор const. Если объект не константный, то его можно будет изменить через const_cast даже внутри const метода.
+2
Интересно. Вот в таком примере:
компилятор не может полагаться на то, что значение 'x' у const-объекта *this не будет изменено при вызове ф-ции
В общем-то, играясь с разными опциями компилятора мне не удалось заставить его выполнить common sub-expression elimination, т.е. умножение на 17 он всегда честно делает 2 раза, независимо от опций оптимизации.
int GLOBAL;
struct point
{
int x;
int calc() const {
GLOBAL = x*17;
change();
return x*17;
}
void change() const {
point* self = const_cast<point*>(this);
self->x++;
}
};
компилятор не может полагаться на то, что значение 'x' у const-объекта *this не будет изменено при вызове ф-ции
change()
?В общем-то, играясь с разными опциями компилятора мне не удалось заставить его выполнить common sub-expression elimination, т.е. умножение на 17 он всегда честно делает 2 раза, независимо от опций оптимизации.
0
Есть мнение, что const — довольно бесполезная, а местами даже вредная штука.
Вредность в том что снимая const с одной функции, вы вынуждены бежать по всему коду и снимать const'ы со всего кода который эту функцию вызывает, и так рекурсивно.
И наоборот, захотев добавить const на функцию, вы вынуждены обвешивать const'ами все что она вызывает. Причем в процессе вам может понадобиться потрогать код сторонней библиотеки — страдайте и обмазывайтесь const_cast'ами и mutable'ами.
Теперь про бесполезность. const не дает никаких гарантий вызывающему коду, поэтому непонятно зачем он в сигнатуре.
А не дает никаких гарантий потому что const имеет право изменять:
1. Аргументы. Среди которых (возможно через множество промежуточных звеньев) может оказаться this, только уже без const'а
2. Статические переменные. Среди которых опять же может оказаться this.
3. mutable, рассмотренный в этой статье.
4. И вообще, можно же взять и memset зафигачить. Что-то где-то поменяется.
5. Самое печальное, что были прецеденты когда это непредумышленно происходило. И ложная надежда «const значит this не поменяется» только мешала.
Вредность в том что снимая const с одной функции, вы вынуждены бежать по всему коду и снимать const'ы со всего кода который эту функцию вызывает, и так рекурсивно.
И наоборот, захотев добавить const на функцию, вы вынуждены обвешивать const'ами все что она вызывает. Причем в процессе вам может понадобиться потрогать код сторонней библиотеки — страдайте и обмазывайтесь const_cast'ами и mutable'ами.
Теперь про бесполезность. const не дает никаких гарантий вызывающему коду, поэтому непонятно зачем он в сигнатуре.
А не дает никаких гарантий потому что const имеет право изменять:
1. Аргументы. Среди которых (возможно через множество промежуточных звеньев) может оказаться this, только уже без const'а
2. Статические переменные. Среди которых опять же может оказаться this.
3. mutable, рассмотренный в этой статье.
4. И вообще, можно же взять и memset зафигачить. Что-то где-то поменяется.
5. Самое печальное, что были прецеденты когда это непредумышленно происходило. И ложная надежда «const значит this не поменяется» только мешала.
-7
const нужен в качестве:
1. дополнительной проверки корректности программ компилятором. Да, грязные хаки могут менять состояние константного объекта, но в корректном коде они не нужны и практически отсутствуют (за исключением mutable примитивов синхронизации). А вот очевидные ошибки/опечатки будут своевременно отловлены.
2. дополнительной информации об API для вызывающей стороны.
3. перегрузка по const/non-const позволяет проводить некоторые оптимизации.
для этого и существуют mutable/const_cast. Зловредную библиотеку можно вызывать через const-корректный адаптер
1. дополнительной проверки корректности программ компилятором. Да, грязные хаки могут менять состояние константного объекта, но в корректном коде они не нужны и практически отсутствуют (за исключением mutable примитивов синхронизации). А вот очевидные ошибки/опечатки будут своевременно отловлены.
2. дополнительной информации об API для вызывающей стороны.
3. перегрузка по const/non-const позволяет проводить некоторые оптимизации.
Причем в процессе вам может понадобиться потрогать код сторонней библиотеки — страдайте.
для этого и существуют mutable/const_cast. Зловредную библиотеку можно вызывать через const-корректный адаптер
+3
1. Я вам целый список предоставил, способов которыми объект может измениться в результате вызова const-метода. Из них грязным хаком я бы назвал const_cast. Но вообще-то все эти механизмы входят в стандарт и UB, например, не вызывают.
2. Какой дополнительной информации? Вы точно прочитали текст на который отвечаете?
3. О, спасибо что напомнили. Запишите это тоже в минус: необходимость дублирования кода для const/не-const бесит, приводит к ошибкам, часто содержит в себе const_cast или является темплейтом (что привносит свои, темплейтные проблемы)
4. Она не зловредная. Она просто не расставила const везде где это возможно (и никто этого не делает, вы в том числе). Хотя бы просто потому что у нее не было инструмента который бы это сделал.
2. Какой дополнительной информации? Вы точно прочитали текст на который отвечаете?
3. О, спасибо что напомнили. Запишите это тоже в минус: необходимость дублирования кода для const/не-const бесит, приводит к ошибкам, часто содержит в себе const_cast или является темплейтом (что привносит свои, темплейтные проблемы)
4. Она не зловредная. Она просто не расставила const везде где это возможно (и никто этого не делает, вы в том числе). Хотя бы просто потому что у нее не было инструмента который бы это сделал.
-2
1. использование любого из этих методов не по прямому назначению подходит под правила дурного тона и является легитимным основанием отклонить ваш код на ревью. memcpy в с++ практически никогда не нужен.
2. например, если в библиотеке есть функция, принимающая const T &val, она (с поправкой на профпригодность автора) не будет менять val.
3. к каким ошибкам?
4. всегда пишу const-корректный код. За последний год использовал const_cast единожды (и то в вызове сишной библиотеки). Мозг, глаза и руки — достаточный набор инструментов для написания корректных программ.
Я уже не говорю про то, что иногда const-корректность необходима для достижения желаемого поведения
п.с. есть еще noexcept, который, между прочим, может оптимизировать выполнение некоторых операций. Его вы тоже не рекомендуете к использованию только потому, что лень печатать?
2. например, если в библиотеке есть функция, принимающая const T &val, она (с поправкой на профпригодность автора) не будет менять val.
3. к каким ошибкам?
4. всегда пишу const-корректный код. За последний год использовал const_cast единожды (и то в вызове сишной библиотеки). Мозг, глаза и руки — достаточный набор инструментов для написания корректных программ.
Я уже не говорю про то, что иногда const-корректность необходима для достижения желаемого поведения
п.с. есть еще noexcept, который, между прочим, может оптимизировать выполнение некоторых операций. Его вы тоже не рекомендуете к использованию только потому, что лень печатать?
+4
Статические переменные. Среди которых опять же может оказаться this.
Можно пример того, как this оказывается статической переменной?
+2
Указатель со значением, равным this, вполне может лежать в статической переменной. Думаю, именно это и имелось в виду.
+1
Впечатления от таких вопросов и ответов у меня двоякие. С одной стороны, задавать подобные вопросы на собеседовании — дело бесполезное, это почти ничего не говорит о способностях интервьюируемого
При написании const-корректных и многопоточных программ, mutable рано или поздно (скорее рано) понадобится. На мой взгляд, претендент на позицию с++ миддла не обязан знать все тонкости с++11/14/17, но базовые вещи из с++03 знать обязан
+1
Имхо, mutable нужно редко, но в тех законных случаях, когда да — без него не обойтись.
0
Я бы отнес mutable (как и const_cast, memset, raw new, и многое другое) к разряду «грязных хаков». Да, ими вполне можно и даже нужно пользоваться, но относительно редко, и, как правило, лишь для цели достижения максимальной производительности и лишь в некоторых служебных местах. Поэтому любое их использование должно быть надежно инкапсулировано.
0
Sign up to leave a comment.
Ключевое слово «mutable» в C++