Продай мне по константной корректности


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

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

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

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

16 99

16 ответов:

Это окончательная статья о "корректности const": https://isocpp.org/wiki/faq/const-correctness .

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

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

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

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

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

void foo(const int DEFCON)
{
   if (DEFCON = 1)     //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
   {
       fire_missiles();
   }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#include <string>

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

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}