Скільки і для чого використовує “const” у C ++?


129

Як початківець програміст на C ++, є деякі конструкції, які мені здаються дуже незрозумілими, одна з них - це const. Ви можете використовувати його в дуже багатьох місцях і з такою кількістю різних ефектів, що початківцю практично неможливо вийти живим. Чи пояснить якийсь гуру С ++ один раз назавжди різні способи використання та чи / та чому їх не використовувати?


точно шукаю це питання: D
аламін

Відповіді:


100

Спроба зібрати деякі використання:

Прив’язання деякого тимчасового до посилання на 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 для класів "копіювати при записі" , щоб компілятор допоміг вам вирішити, коли і коли не потрібно копіювати.

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, тому що використана бібліотека C ++ реалізує копіювання під час запису std::string. Обидва рядки, навіть якщо це окремі об'єкти, мають однакову пам'ять для своїх рядкових даних. Якщо зробити bnon-const, то віддасть перевагу 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
Чи можете ви пояснити, будь ласка, перше та третє використання у своїх прикладах?
tunnuz

"Для того, щоб гарантувати абоненту, що параметр не може бути NULL", я не бачу, як const має щось спільне з цим прикладом.
Логан Капальдо

oops, я так не вдається. я якось почав писати про посилання. велике спасибі за стогін :) Я, звичайно, зараз зніму цей матеріал :)
Йоханнес Шауб - ліб

3
Будь ласка, поясніть перший приклад. Для мене це не має великого сенсу.
чикуба

28

У C ++ дійсно є 2 основні сфери використання const.

Ціннісні значення

Якщо значення має форму змінної, члена або параметра, який не буде (або не повинен) змінюватися протягом свого життя, слід позначити його const. Це допомагає запобігти мутаціям на об’єкті. Наприклад, у наступній функції мені не потрібно змінювати переданий екземпляр Student, тому я відмічаю це const.

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

Щодо того, чому б ти це робив. Набагато простіше міркувати про алгоритм, якщо ви знаєте, що базові дані не можуть змінюватися. "const" допомагає, але не гарантує, що це буде досягнуто.

Очевидно, що друк даних у cout не потребує багато роздумів :)

Позначення методу члена як const

У попередньому прикладі я позначив Student як const. Але як C ++ знав, що виклик методу GetName () на студента не буде мутувати об'єкт? Відповідь полягає в тому, що метод був позначений як const.

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

Позначення методу "const" робить 2 речі. Перш за все це говорить C ++, що цей метод не буде мутувати мій об’єкт. Друга річ полягає в тому, що всі змінні учасники тепер будуть розглядатися так, ніби вони позначені як const. Це допомагає, але не заважає вам змінювати примірник вашого класу.

Це надзвичайно простий приклад, але, сподіваємось, він допоможе відповісти на ваші запитання.


16

Не забудьте зрозуміти різницю між цими 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


Окольним чином, так, це так! Правило спіралі за годинниковою стрілкою описує це краще - починайте з імені (kpPointer) і намалюйте спіраль за годинниковою стрілкою, що виходить через маркер, і вимовіть кожен маркер. Очевидно, що немає праворуч від kpPointer, але він все одно працює.
Стів Фоллі

3

Як невеличку замітку, коли я читаю тут , це корисно помітити

const застосовується до того, що знаходиться зліва зліва (за винятком випадків, коли там немає нічого, в цьому випадку воно стосується того, що є його безпосереднім правом).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.