Penunjuk cerdas (Modern C++)

Dalam pemrograman C++ modern, Pustaka Standar mencakup penunjuk cerdas, yang digunakan untuk membantu memastikan bahwa program bebas dari kebocoran memori dan sumber daya dan aman pengecualian.

Menggunakan untuk penunjuk cerdas

Penunjuk cerdas didefinisikan dalam std namespace dalam <file header memori> . Ini sangat penting bagi RAII atau Akuisisi Sumber Daya adalah idiom pemrograman Inisialisasi . Tujuan utama dari idiom ini adalah untuk memastikan bahwa akuisisi sumber daya terjadi pada saat yang sama ketika objek diinisialisasi, sehingga semua sumber daya untuk objek dibuat dan dibuat siap dalam satu baris kode. Dalam istilah praktis, prinsip utama RAII adalah memberikan kepemilikan atas sumber daya yang dialokasikan timbunan—misalnya, memori yang dialokasikan secara dinamis atau handel objek sistem—ke objek yang dialokasikan tumpukan yang destruktornya berisi kode untuk menghapus atau membebaskan sumber daya dan juga kode pembersihan terkait.

Dalam kebanyakan kasus, saat Anda menginisialisasi pointer mentah atau handel sumber daya untuk menunjuk ke sumber daya aktual, teruskan penunjuk ke penunjuk cerdas segera. Dalam C++modern, pointer mentah hanya digunakan dalam blok kode kecil dari cakupan terbatas, perulangan, atau fungsi pembantu di mana performa sangat penting dan tidak ada kemungkinan kebingungan tentang kepemilikan.

Contoh berikut membandingkan deklarasi pointer mentah dengan deklarasi pointer pintar.

void UseRawPointer()
{
    // Using a raw pointer -- not recommended.
    Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); 

    // Use pSong...

    // Don't forget to delete!
    delete pSong;   
}

void UseSmartPointer()
{
    // Declare a smart pointer on stack and pass it the raw pointer.
    unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));

    // Use song2...
    wstring s = song2->duration_;
    //...

} // song2 is deleted automatically here.

Seperti yang ditunjukkan dalam contoh, penunjuk cerdas adalah templat kelas yang Anda deklarasikan di tumpukan, dan diinisialisasi dengan menggunakan pointer mentah yang menunjuk ke objek yang dialokasikan tumpukan. Setelah penunjuk pintar diinisialisasi, ia memiliki pointer mentah. Ini berarti bahwa penunjuk pintar bertanggung jawab untuk menghapus memori yang ditentukan penunjuk mentah. Destruktor penunjuk pintar berisi panggilan untuk dihapus, dan karena penunjuk pintar dideklarasikan pada tumpukan, destruktornya dipanggil ketika penunjuk pintar keluar dari cakupan, bahkan jika pengecualian dilemparkan di suatu tempat lebih jauh ke tumpukan.

Akses penunjuk yang dienkapsulasi dengan menggunakan operator penunjuk yang familier, -> dan *, yang kelebihan kelas penunjuk pintar untuk mengembalikan pointer mentah yang dienkapsulasi.

Idiom penunjuk pintar C++ menyerupan pembuatan objek dalam bahasa seperti C#: Anda membuat objek dan kemudian membiarkan sistem mengurus penghapusannya pada waktu yang benar. Perbedaannya adalah bahwa tidak ada pengumpul sampah terpisah yang berjalan di latar belakang; memori dikelola melalui aturan cakupan C++ standar sehingga lingkungan runtime lebih cepat dan lebih efisien.

Penting

Selalu buat penunjuk cerdas pada baris kode terpisah, tidak pernah dalam daftar parameter, sehingga kebocoran sumber daya yang halus tidak akan terjadi karena aturan alokasi daftar parameter tertentu.

Contoh berikut menunjukkan bagaimana unique_ptr jenis penunjuk pintar dari Pustaka Standar C++ dapat digunakan untuk merangkum pointer ke objek besar.


class LargeObject
{
public:
    void DoSomething(){}
};

void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{    
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass a reference to a method.
    ProcessLargeObject(*pLarge);

} //pLarge is deleted automatically when function block goes out of scope.

Contoh menunjukkan langkah-langkah penting berikut untuk menggunakan penunjuk cerdas.

  1. Deklarasikan penunjuk pintar sebagai variabel otomatis (lokal). (Jangan gunakan new ekspresi atau malloc pada penunjuk pintar itu sendiri.)

  2. Dalam parameter jenis, tentukan jenis pointed-to dari pointer yang dienkapsulasi.

  3. Teruskan pointer mentah ke newobjek -ed di konstruktor penunjuk pintar. (Beberapa fungsi utilitas atau konstruktor penunjuk cerdas melakukan ini untuk Anda.)

  4. Gunakan operator dan * yang kelebihan -> beban untuk mengakses objek.

  5. Biarkan penunjuk pintar menghapus objek.

Pointer pintar dirancang agar seefisien mungkin baik dalam hal memori maupun performa. Misalnya, satu-satunya anggota data di adalah penunjuk yang dienkapsulasi unique_ptr . Ini berarti ukuran yang unique_ptr sama persis dengan pointer tersebut, baik empat byte atau delapan byte. Mengakses penunjuk yang dienkapsulasi dengan menggunakan pointer pintar yang kelebihan beban * dan -> operator tidak secara signifikan lebih lambat daripada mengakses pointer mentah secara langsung.

Pointer pintar memiliki fungsi anggota mereka sendiri, yang diakses dengan menggunakan notasi "titik". Misalnya, beberapa penunjuk pintar Pustaka Standar C++ memiliki fungsi reset anggota yang merilis kepemilikan pointer. Ini berguna ketika Anda ingin membebaskan memori yang dimiliki oleh penunjuk pintar sebelum penunjuk pintar keluar dari cakupan, seperti yang ditunjukkan dalam contoh berikut.

void SmartPointerDemo2()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Free the memory before we exit function block.
    pLarge.reset();

    // Do some other work...

}

Pointer pintar biasanya menyediakan cara untuk mengakses pointer mentah mereka secara langsung. Pointer pintar Pustaka Standar C++ memiliki get fungsi anggota untuk tujuan ini, dan CComPtr memiliki anggota kelas publik p . Dengan menyediakan akses langsung ke penunjuk yang mendasar, Anda dapat menggunakan penunjuk cerdas untuk mengelola memori dalam kode Anda sendiri dan masih meneruskan pointer mentah ke kode yang tidak mendukung penunjuk pintar.

void SmartPointerDemo4()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass raw pointer to a legacy API
    LegacyLargeObjectFunction(pLarge.get());    
}

Jenis penunjuk cerdas

Bagian berikut ini meringkas berbagai jenis penunjuk pintar yang tersedia di lingkungan pemrograman Windows dan menjelaskan kapan harus menggunakannya.

Penunjuk pintar Pustaka Standar C++

Gunakan pointer pintar ini sebagai pilihan pertama untuk merangkum pointer ke objek C++ lama biasa (POCO).

  • unique_ptr
    Memungkinkan tepat satu pemilik pointer yang mendasar. Gunakan sebagai pilihan default untuk POCO kecuali Anda tahu dengan pasti bahwa Anda memerlukan shared_ptr. Dapat dipindahkan ke pemilik baru, tetapi tidak disalin atau dibagikan. auto_ptrMenggantikan , yang tidak digunakan lagi. boost::scoped_ptrBandingkan dengan . unique_ptr kecil dan efisien; ukurannya adalah satu penunjuk dan mendukung referensi rvalue untuk penyisipan dan pengambilan cepat dari koleksi Pustaka Standar C++. File header: <memory>. Untuk informasi selengkapnya, lihat Cara: Membuat dan Menggunakan Instans unique_ptr dan Kelas unique_ptr.

  • shared_ptr
    Penunjuk pintar yang dihitung referensi. Gunakan saat Anda ingin menetapkan satu penunjuk mentah ke beberapa pemilik, misalnya, saat Anda mengembalikan salinan pointer dari kontainer tetapi ingin mempertahankan yang asli. Pointer mentah tidak dihapus sampai semua shared_ptr pemilik keluar dari cakupan atau menyerahkan kepemilikan. Ukurannya adalah dua pointer; satu untuk objek dan satu untuk blok kontrol bersama yang berisi jumlah referensi. File header: <memory>. Untuk informasi selengkapnya, lihat Cara: Membuat dan Menggunakan Instans shared_ptr dan Kelas shared_ptr.

  • weak_ptr
    Pointer pintar kasus khusus untuk digunakan bersama dengan shared_ptr. menyediakan weak_ptr akses ke objek yang dimiliki oleh satu atau beberapa shared_ptr instans, tetapi tidak berpartisipasi dalam penghitungan referensi. Gunakan saat Anda ingin mengamati objek, tetapi tidak mengharuskannya untuk tetap hidup. Diperlukan dalam beberapa kasus untuk memutus referensi melingkar antar shared_ptr instans. File header: <memory>. Untuk informasi selengkapnya, lihat Cara: Membuat dan Menggunakan Instans weak_ptr dan Kelas weak_ptr.

Penunjuk cerdas untuk objek COM (pemrograman Windows klasik)

Saat Anda bekerja dengan objek COM, bungkus penunjuk antarmuka dalam jenis penunjuk cerdas yang sesuai. Pustaka Templat Aktif (ATL) mendefinisikan beberapa penunjuk cerdas untuk berbagai tujuan. Anda juga dapat menggunakan _com_ptr_t jenis penunjuk pintar, yang digunakan pengkompilasi saat membuat kelas pembungkus dari file .tlb. Ini adalah pilihan terbaik ketika Anda tidak ingin menyertakan file header ATL.

Kelas CComPtr
Gunakan ini kecuali Anda tidak dapat menggunakan ATL. Melakukan penghitungan referensi dengan menggunakan AddRef metode dan Release . Untuk informasi selengkapnya, lihat Cara: Membuat dan Menggunakan Instans CComPtr dan CComQIPtr.

Kelas CComQIPtr
Menyerupan CComPtr tetapi juga menyediakan sintaks yang disederhanakan untuk memanggil QueryInterface pada objek COM. Untuk informasi selengkapnya, lihat Cara: Membuat dan Menggunakan Instans CComPtr dan CComQIPtr.

Kelas CComHeapPtr
Penunjuk cerdas ke objek yang digunakan CoTaskMemFree untuk membebaskan memori.

Kelas CComGITPtr
Penunjuk cerdas untuk antarmuka yang diperoleh dari tabel antarmuka global (GIT).

Kelas _com_ptr_t
Menyerupan CComQIPtr dalam fungsionalitas tetapi tidak bergantung pada header ATL.

Pointer pintar ATL untuk objek POCO

Selain penunjuk cerdas untuk objek COM, ATL juga mendefinisikan penunjuk cerdas, dan koleksi penunjuk pintar, untuk objek C++ lama biasa (POCO). Dalam pemrograman Windows klasik, jenis ini adalah alternatif yang berguna untuk koleksi Pustaka Standar C++, terutama ketika portabilitas kode tidak diperlukan atau ketika Anda tidak ingin mencampur model pemrograman Pustaka Standar C++ dan ATL.

Kelas CAutoPtr
Penunjuk cerdas yang memberlakukan kepemilikan unik dengan mentransfer kepemilikan pada salinan. Sebanding dengan Kelas yang tidak digunakan std::auto_ptr lagi.

Kelas CHeapPtr
Penunjuk cerdas untuk objek yang dialokasikan dengan menggunakan fungsi C malloc .

Kelas CAutoVectorPtr
Penunjuk cerdas untuk array yang dialokasikan dengan menggunakan new[].

Kelas CAutoPtrArray
Kelas yang merangkum array CAutoPtr elemen.

Kelas CAutoPtrList
Kelas yang merangkum metode untuk memanipulasi daftar CAutoPtr simpul.

Baca juga

Pointer
Referensi Bahasa C++
Pustaka Standar C++