Продай меня за правильность констант

133

Так почему же всегда рекомендуется использовать const как можно чаще? Мне кажется, что использование const может быть больше проблемой, чем помочь в C ++. Но опять же, я подхожу к этому с точки зрения Python: если вы не хотите, чтобы что-то изменялось, не меняйте это. С учетом сказанного, вот несколько вопросов:

  1. Кажется, что каждый раз, когда я помечаю что-то как const, я получаю сообщение об ошибке и мне приходится где-то менять какую-то другую функцию, чтобы она тоже была const. Тогда это заставляет меня менять другую функцию в другом месте. Разве это становится легче с опытом?

  2. Являются ли преимущества использования сопза действительно достаточно , чтобы компенсировать проблемы? Если вы не собираетесь изменять объект, почему бы просто не написать код, который его не меняет?

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

Джейсон Бейкер
источник
1
8-летняя ретроспектива, но .. Как насчет того, чтобы ускорить ваш код в 100 раз (видел это вживую)?
lorro
1
Проверьте мой ответ здесь (связанный вопрос, я бы сказал тот же ответ): stackoverflow.com/questions/42310477/… Кстати: я люблюconst
Джинес Идальго

Ответы:

158

Это исчерпывающая статья о «корректности констант»: https://isocpp.org/wiki/faq/const-correctness .

Короче говоря, использование const - хорошая практика, потому что ...

  1. Он защищает вас от случайного изменения переменных, которые не предполагается изменять,
  2. Он защищает вас от случайного присвоения переменных и
  3. Компилятор может его оптимизировать. Например, вы защищены от

    if( x = y ) // whoops, meant if( x == y )

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

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

Джордан Пармер
источник
Я всегда предполагал, что значения const можно свободно кэшировать и, таким образом, избежать многих проблем с параллелизмом или повысить скорость. Это правда?
Phil H
3
Конечно. constпеременные могут повысить скорость в том смысле, что компилятор может генерировать более оптимальный код, поскольку он знает полное назначение и использование переменной. Вы заметите разницу? Ну, это спорно при подаче заявки. Что касается параллелизма, константные переменные доступны только для чтения, что означает, что нет необходимости в монопольных блокировках этих переменных, поскольку значение всегда будет одинаковым.
Джордан Пармер,
4
Начиная с C ++ 11 стандартная библиотека также подразумевает constпоточно-ориентированную обработку, и обработка вашего собственного кода таким образом упрощает проверку возможных проблем с многопоточностью и является простым способом предоставления гарантий API для пользователей вашего API.
pmr
3
Для меня одно из самых больших преимуществ правильности констант заключается в том, что вы узнаете, просто взглянув на прототипы функций, когда указатели ссылаются на данные, которые никогда не изменяются функцией (т.е. только ввод).
cp.engr
1
Не забывайте, что есть логическая и физическая постоянство. Любой содержащийся агрегированный объект, помеченный как изменяемый, может измениться, хотя его пометка как таковая подразумевает, что он не имеет ничего общего с логической константностью содержащего объекта.
Адриан
128

Вот фрагмент кода с распространенной ошибкой, от которой вас может защитить корректность const:

void foo(const int DEFCON)
{
   if (DEFCON = 1)     //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
   {
       fire_missiles();
   }
}
Дуг Т.
источник
19
Итак, мы говорим, что const необходима, потому что какой-то чертов идиот выбрал "=" в качестве оператора присваивания в C? ;-)
Стив Джессоп
37
Конечно, большинство современных компиляторов предупреждают о назначениях в условных выражениях, и мы все включаем обработку предупреждений как флагов ошибок, верно: ->
Джек Болдинг
7
Не будьте слишком усердны в этом примере: это тот же пример, который пропагандируют некоторые разработчики, чтобы убедить нас использовать if (0 == i) вместо if (i == 0). Наконец, шаблон кода Дуга можно использовать вне оператора if. Важно то, что он юмористически показывает одно из преимуществ const. + 1.
paercebal
2
Некоторые компиляторы (и линты) уже давно предупреждают об этом, и это гораздо полезнее для обнаружения этой опечатки, чем const: codepad.org/Nnf5JUXV .
7
@Roger, вы предполагаете, что мы живем в мире, где сборки чисты и не содержат предупреждений. Мой опыт показывает, что во многих кодах предупреждения теряются в шуме. Кроме того, есть много кода, который выполняет присваивания в выражении if, и многие будут утверждать, что это правильный, «хороший» стиль.
Дуг Т.
62

Кажется, что каждый раз, когда я помечаю что-то как const, я получаю сообщение об ошибке и мне приходится где-то менять какую-то другую функцию, чтобы она тоже была const. Тогда это заставляет меня менять другую функцию где-то еще. Разве это становится легче с опытом?

По опыту, это полный миф. Конечно, это происходит, когда неконстантно-правильный код находится с константно-правильным кодом. Если вы с самого начала разрабатываете const-corrective, это НИКОГДА не должно быть проблемой. Если вы делаете что-то const, а затем что-то еще не компилируется, компилятор сообщает вам что-то чрезвычайно важное, и вы должны найти время, чтобы исправить это должным образом .

Крис Майер
источник
7
Это помогло мне предотвратить так много ошибок, когда у меня была константная функция, которая собиралась вызвать неконстантную функцию, потому что я забыл, что она изменяет данные ниже.
Mooing Duck
9
Очень немногие люди всегда начинают с чистого кода. Большая часть кода - это обслуживание устаревшего кода, где цитируемый комментарий довольно точен. Я использую const при написании новых листовых функций, но сомневаюсь, стоит ли гоняться за вещами через полдюжины или дюжину уровней вызова незнакомого кода.
Уоррен Дью
3
По моему опыту, это совершенно неверно. Вложенные типы точек создают самую большую головную боль для такого рода вещей, когда у вас может быть несколько констант-квантификаторов в одном типе.
Нолдорин
4
«Если вы проектируете константную правильность с самого начала», сколько раз у вас действительно есть роскошь форсировать константную корректность с самого начала ?? Большинству из нас ежедневно приходится иметь дело с многочисленными API и библиотеками, хотя это не так, и вы мало что можете с этим поделать. Так что я согласен с мнением ОП: «Кажется, каждый раз, когда я отмечаю что-то как const, я получаю сообщение об ошибке» ...
nyholku
@WarrenDew Это переходный аргумент; если бы исходные авторы использовали constправильно (т.е. «с самого начала»), тогда действительно не было бы проблемы, в чем именно суть!
Гонки
31

Когда вы пишете код изначально, это не для вас. Это для кого-то другого (или вас несколько месяцев спустя), который просматривает объявление метода внутри класса или интерфейса, чтобы увидеть, что он делает. Отказ от модификации объекта - важная часть информации, которую можно извлечь из этого.

Антонио Хейли
источник
1
Это только отчасти правда. Const используется как защита, а также для принудительного применения переменных через интерфейсы и так далее.
Джордан Пармер,
Это так, но вы можете добиться этого с помощью дисциплинированной практики кодирования, и на плакате говорится. Реальная выгода заключается в том, что это отражено в API.
Антонио Хейли,
2
Именно. Лучше иметь "const int Value = 5;" чем "int ConstValue = 5;". +1.
paercebal
6
Подходящее время для добавления константной корректности - это когда вы изначально пишете API. В противном случае у вас возникнут проблемы с константным отравлением, когда вы его модернизируете (поэтому добавление его в старый код - это совершенно другое дело , чем в новом коде).
Donal Fellows
@DonalFellows это не значит, что вставьте const позже. Это говорит о том, что вы получите преимущества позже, когда прочитаете код с уже присутствующей const
Калет
27

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

QBziZ
источник
1
так как C ++ 17 и совершенство constexpr, я пишу модульные тесты времени компиляции ... все ближе ...
QBziZ
22

const - это обещание, которое вы даете как разработчик и заручаетесь помощью компилятора в обеспечении соблюдения.

Мои причины быть правильными:

  • Он сообщает клиентам вашей функции, что вы не будете изменять переменную или объект.
  • Принятие аргументов по константной ссылке дает вам эффективность передачи по ссылке с безопасностью передачи по значению.
  • Написание ваших интерфейсов как правильных констант позволит клиентам использовать их. Если вы пишете свой интерфейс так, чтобы он принимал неконстантные ссылки, клиентам, использующим константу, потребуется отбросить константу, чтобы работать с вами. Это особенно раздражает, если ваш интерфейс принимает неконстантные char *, а ваши клиенты используют std :: strings, поскольку вы можете получить только от них const char *.
  • Использование const заставит компилятор поддерживать вашу честность, чтобы вы по ошибке не изменили то, что не должно измениться.
JohnMcG
источник
20

Моя философия заключается в том, что если вы собираетесь использовать придирчивый язык с проверками времени компиляции, используйте его как можно лучше. const- это принудительный способ передать то, что вы имеете в виду, компилятор ... он лучше, чем комментарии или doxygen. Вы платите цену, почему бы не получить ценность?

Пэт Нотц
источник
20

Программирование на C ++ без const похоже на вождение без ремня безопасности.

Больно пристегивать ремень безопасности каждый раз, когда вы садитесь в машину, и 364 дня из 365 вы приедете благополучно.

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

Андреас Buykx
источник
18

Для встроенного программирования, используя const разумное при объявлении глобальных структур данных может сэкономить много оперативной памяти, заставляя постоянные данные располагаться в ПЗУ или флэш-памяти без копирования в ОЗУ во время загрузки.

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

При работе с другими программистами над крупными проектами constправильное использование помогает предотвратить удушение другими программистами.

bk1e
источник
13

constпомогает изолировать код, который «меняет вещи» за вашей спиной. Итак, в классе вы должны отметить все методы, которые не изменяют состояние объекта как const. Это означает, что constэкземпляры этого класса больше не смогут вызывать никакие не-const методы, методами. Таким образом, вы не сможете случайно вызвать функциональность, которая может изменить ваш объект.

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

Пример:

#include <iostream>

class HelloWorld {
    bool hw_called;

public:
    HelloWorld() : hw_called(false) {}

    void hw() const {
        std::cout << "Hello, world! (const)\n";
        // hw_called = true;  <-- not allowed
    }

    void hw() {
        std::cout << "Hello, world! (non-const)\n";
        hw_called = true;
    }
};

int
main()
{
    HelloWorld hw;
    HelloWorld* phw1(&hw);
    HelloWorld const* phw2(&hw);

    hw.hw();    // calls non-const version
    phw1->hw(); // calls non-const version
    phw2->hw(); // calls const version
    return 0;
}
Крис Шут-Янг
источник
13

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

В большей части кода, который я пишу, усилия стоили того, потому что мы часто используем композицию:

class A { ... }
class B { A m_a; const A& getA() const { return m_a; } };

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

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

Питер Ковач
источник
7

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

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

Грег Роджерс
источник
4

Существует хорошая статья здесь о Const в C ++. Это довольно прямое мнение, но надеюсь, что оно кому-то поможет.

Олафур Вааге
источник
3

Когда вы используете ключевое слово «const», вы определяете другой интерфейс для своих классов. Есть интерфейс, который включает все методы, и интерфейс, который включает только константные методы. Очевидно, это позволяет вам ограничить доступ к некоторым вещам, которые вы не хотите изменять.

Да, со временем становится легче.

Уэсли Тарл
источник
2

Мне нравится константная корректность ... теоретически. Каждый раз, когда я пытался применить его строго на практике, он в конце концов ломался, и const_cast начинает постепенно превращаться в уродливый код.

Может быть, я использую просто шаблоны проектирования, но const всегда оказывается слишком широкой кистью.

Например, представьте себе простой механизм базы данных ... у него есть объекты схемы, таблицы, поля и т. Д. Пользователь может иметь указатель 'const Table', означающий, что им не разрешено изменять саму схему таблицы ... но как насчет манипулирования данные, связанные с таблицей? Если метод Insert () помечен как константа, то внутри он должен отбросить константу, чтобы фактически управлять базой данных. Если он не помечен как const, он не защищает от вызова метода AddField.

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

Роб Уокер
источник
Я думаю, что ваш пример - это случай чрезмерного использования константы, но не забывайте о модификаторе mutable, который можно применять к переменным экземпляра, чтобы удалить константу.
Zooba,
2
Когда функция Insert () может быть помечена как const, если она не имеет неправильного названия? Добавление вещей обычно изменяет то, к чему вы его добавили, то есть это не const. Что вам действительно нужно, так это TableWithConstSchema.
Грег Роджерс,
Я мог бы добавить еще один класс, но я не хочу делать API излишне сложным только ради константной корректности.
Роб Уокер,
1

Вы также можете давать подсказки компилятору с помощью const .... в соответствии со следующим кодом

#include <string>

void f(const std::string& s)
{

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}
Кит Николас
источник