Сколько и какие использует "const" в С++?

Как начинающий программист на С++ есть некоторые конструкции, которые кажутся мне очень неясными, один из них - const. Вы можете использовать его во многих местах и ​​с таким количеством различных эффектов, что почти невозможно для новичка выжить живым. Будет ли какой-нибудь С++-гуру раз и навсегда объяснять различные виды использования и есть ли и/или почему бы не использовать их?

Ответ 1

Попытка собрать некоторые виды использования:

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

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

Объяснение, используя код:

struct ScopeGuard { 
    ~ScopeGuard() { } // not virtual
};

template<typename T> struct Derived : ScopeGuard { 
    T t; 
    Derived(T t):t(t) { }
    ~Derived() {
        t(); // call function
    }
};

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); }

Этот трюк используется в утилите Alexandrescu ScopeGuard. После временного выхода из области действия деструктор Derived вызывается правильно. Вышеприведенный код пропускает некоторые мелкие детали, но это связано с этим.


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

struct SmartPtr {
    int getCopies() const { return mCopiesMade; }
};

Использовать const для классов copy-on-write, чтобы компилятор помог вам решить, когда и когда вам не нужно копировать.

struct MyString {
    char * getData() { /* copy: caller might write */ return mData; }
    char const* getData() const { return mData; }
};

Объяснение: Возможно, вам захочется обмениваться данными при копировании, если данные изначально и объекта copie'd остаются неизменными. После того, как один из объектов изменит данные, вам понадобятся теперь две версии: одна для оригинала и одна для копии. То есть, вы копируете на запись на любой объект, чтобы теперь у них была своя версия.

Использование кода:

int main() {
    string const a = "1234";
    string const b = a;
    // outputs the same address for COW strings
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

Вышеприведенный фрагмент печатает тот же адрес в моем GCC, потому что используемая библиотека С++ реализует copy-on-write std::string. Обе строки, даже если они являются отдельными объектами, используют одну и ту же память для своих строковых данных. Создание b non-const будет предпочитать неконстантную версию operator[], а GCC создаст копию буфера памяти поддержки, потому что мы можем ее изменить и не должны влиять на данные a!

int main() {
    string const a = "1234";
    string b = a;
    // outputs different addresses!
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

Чтобы экземпляр-копир делал копии из объектов const и временных файлов:

struct MyClass {
    MyClass(MyClass const& that) { /* make copy of that */ }
};

Для создания констант, которые тривиально не могут изменить

double const PI = 3.1415;

Для передачи произвольных объектов по ссылке вместо значения - для предотвращения возможной дорогостоящей или невозможной передачи значения

void PrintIt(Object const& obj) {
    // ...
}

Ответ 2

В С++ существует 2 основных использования const.

Значения констант

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

void PrintStudent(const Student& student) {
  cout << student.GetName();
}

Как вы это сделаете? Гораздо проще рассуждать об алгоритме, если вы знаете, что базовые данные не могут измениться. "const" помогает, но не гарантирует, что это будет достигнуто.

Очевидно, что для печати данных в cout не требуется много размышлений:)

Пометка метода-члена как const

В предыдущем примере я отмечен как "Студент" как const. Но как С++ знал, что вызов метода GetName() для ученика не будет мутировать объект? Ответ заключается в том, что метод был отмечен как const.

class Student {
  public:
    string GetName() const { ... }
};

Маркировка метода "const" делает 2 вещи. В первую очередь он сообщает С++, что этот метод не будет мутировать мой объект. Во-вторых, все переменные-члены теперь будут обрабатываться так, как если бы они были помечены как const. Это помогает, но не мешает вам изменять экземпляр вашего класса.

Это очень простой пример, но, надеюсь, он поможет ответить на ваши вопросы.

Ответ 3

Позаботьтесь о различии между этими 4 объявлениями:

Следующие 2 объявления идентичны семантически. Вы можете изменить точку ccp1 и ccp2, но вы не можете изменить то, на что они указывают.

const char* ccp1;
char const* ccp2;

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

char* const cpc = &something_possibly_not_const;

Наконец, мы объединим два - так что указываемая вещь не может быть изменена, а указатель не может указывать нигде.

const char* const ccpc = &const_obj;

Правило по часовой стрелке может помочь распутать объявление http://c-faq.com/decl/spiral.anderson.html

Ответ 4

В качестве небольшого примечания, когда я читаю здесь, полезно заметить, что

const применяется к тому, что находится на его непосредственном левом (кроме if там нет ничего, в этом случае это относится ко всему, что есть немедленное право).