Berapa banyak dan yang merupakan penggunaan "const" di C ++?

129

Sebagai seorang programmer C ++ pemula ada beberapa konstruksi yang terlihat masih sangat tidak jelas bagi saya, salah satunya adalah const. Anda dapat menggunakannya di banyak tempat dan dengan begitu banyak efek berbeda yang hampir tidak mungkin bagi seorang pemula untuk keluar hidup-hidup. Akankah beberapa guru C ++ menjelaskan sekali selamanya berbagai kegunaan dan apakah dan / atau mengapa tidak menggunakannya?

tunnuz
sumber
tepatnya mencari pertanyaan itu: D
alamin

Jawaban:

100

Mencoba mengumpulkan beberapa kegunaan:

Mengikat beberapa sementara ke referensi-ke-const, untuk memperpanjang masa pakainya. Referensi dapat berupa basis - dan destruktor tidak perlu virtual - destruktor yang tepat masih disebut:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

Penjelasan , menggunakan kode:

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

Trik ini digunakan dalam kelas utilitas ScopeGuard milik Alexandrescu. Setelah sementara keluar dari ruang lingkup, destructor Derived dipanggil dengan benar. Kode di atas melewatkan beberapa detail kecil, tapi itulah masalahnya.


Gunakan const untuk memberi tahu orang lain bahwa metode tidak akan mengubah keadaan logis objek ini.

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

Gunakan const untuk kelas copy-on-write , untuk membuat kompiler membantu Anda memutuskan kapan dan kapan Anda perlu menyalin.

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

Penjelasan : Anda mungkin ingin berbagi data saat Anda menyalin sesuatu selama data dari objek asli dan copie akan tetap sama. Setelah salah satu objek mengubah data, Anda perlu dua versi sekarang: Satu untuk yang asli, dan satu untuk salinan. Artinya, Anda menyalin pada tulisan ke objek mana pun, sehingga mereka sekarang memiliki versi sendiri.

Menggunakan kode :

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

Cuplikan di atas mencetak alamat yang sama pada GCC saya, karena pustaka C ++ yang digunakan mengimplementasikan copy-on-write std::string. Kedua string, meskipun mereka adalah objek yang berbeda, berbagi memori yang sama untuk data string mereka. Membuat bnon-const akan lebih memilih versi non-const dari operator[]dan GCC akan membuat salinan buffer memori backing, karena kita bisa mengubahnya dan itu tidak akan mempengaruhi data a!

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

Untuk copy-constructor untuk membuat salinan dari objek const dan temporaries :

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

Untuk membuat konstanta yang sepele tidak bisa berubah

double const PI = 3.1415;

Untuk melewati objek yang arbitrer dengan referensi alih-alih berdasarkan nilai - untuk mencegah kemungkinan by-value passing yang mahal atau tidak mungkin

void PrintIt(Object const& obj) {
    // ...
}
Johannes Schaub - litb
sumber
2
Bisakah Anda jelaskan penggunaan pertama dan ketiga dalam contoh Anda?
tunnuz
"Untuk menjamin callee bahwa parameter tidak boleh NULL" Saya tidak melihat bagaimana const ada hubungannya dengan contoh itu.
Logan Capaldo
Ups, saya sangat gagal. saya entah bagaimana mulai menulis tentang referensi. terima kasih banyak untuk mengerang :) saya akan tentu saja menghapus hal-hal yang sekarang :)
Johannes Schaub - litb
3
Tolong jelaskan contoh pertama. Tidak masuk akal bagi saya.
chikuba
28

Sebenarnya ada 2 penggunaan utama const di C ++.

Nilai Konst

Jika suatu nilai dalam bentuk variabel, anggota, atau parameter yang tidak akan (atau tidak boleh) diubah selama masa pakainya, Anda harus menandainya sebagai const. Ini membantu mencegah mutasi pada objek. Sebagai contoh, dalam fungsi berikut ini saya tidak perlu mengubah instance Siswa lulus jadi saya tandai itu const.

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

Mengapa Anda melakukan ini? Jauh lebih mudah untuk berpikir tentang suatu algoritma jika Anda tahu bahwa data yang mendasarinya tidak dapat berubah. "const" membantu, tetapi tidak menjamin ini akan tercapai.

Jelas, mencetak data ke cout tidak membutuhkan banyak pemikiran :)

Menandai metode anggota sebagai const

Pada contoh sebelumnya saya menandai Student sebagai const. Tapi bagaimana C ++ tahu bahwa memanggil metode GetName () pada siswa tidak akan bermutasi objek? Jawabannya adalah bahwa metode itu ditandai sebagai const.

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

Menandai metode "const" melakukan 2 hal. Terutama itu memberitahu C ++ bahwa metode ini tidak akan bermutasi objek saya. Yang kedua adalah bahwa semua variabel anggota sekarang akan diperlakukan seolah-olah mereka ditandai sebagai const. Ini membantu tetapi tidak mencegah Anda dari memodifikasi instance kelas Anda.

Ini adalah contoh yang sangat sederhana tetapi mudah-mudahan ini akan membantu menjawab pertanyaan Anda.

JaredPar
sumber
16

Berhati-hatilah untuk memahami perbedaan antara 4 deklarasi ini:

2 deklarasi berikut identik secara semantik. Anda dapat mengubah mana CCP1 dan CCP2 titik, tetapi Anda tidak dapat mengubah hal yang mereka menunjuk.

const char* ccp1;
char const* ccp2;

Selanjutnya, pointer adalah const, jadi agar bermakna itu harus diinisialisasi untuk menunjuk ke sesuatu. Anda tidak dapat membuatnya menunjuk ke hal lain, namun hal yang ditunjukkannya dapat diubah.

char* const cpc = &something_possibly_not_const;

Akhirnya, kami menggabungkan keduanya - jadi benda yang diarahkan tidak dapat dimodifikasi, dan penunjuk tidak dapat menunjuk ke tempat lain.

const char* const ccpc = &const_obj;

Aturan spiral searah jarum jam dapat membantu mengurai deklarasi http://c-faq.com/decl/spiral.anderson.html

Steve Folly
sumber
Secara tidak langsung, ya! Aturan spiral searah jarum jam menjelaskannya dengan lebih baik - mulai dari nama (kpPointer) dan gambar spiral searah jarum jam keluar melalui token, dan ucapkan setiap token. Jelas, tidak ada apa-apa di sebelah kanan kpPointer tetapi masih berfungsi.
Steve Folly
3

Sebagai catatan kecil, ketika saya baca di sini , ada baiknya memperhatikan hal itu

const berlaku untuk apa pun yang ada di sebelah kiri langsung (selain jika tidak ada di sana dalam hal ini berlaku untuk apa pun yang langsung menjadi haknya).

JoePerkins
sumber