Saya telah bekerja dengan daftar tertaut sebelumnya secara ekstensif di Java, tetapi saya sangat baru mengenal C ++. Saya menggunakan kelas node ini yang diberikan kepada saya dalam sebuah proyek dengan baik
class Node
{
public:
Node(int data);
int m_data;
Node *m_next;
};
tapi saya punya satu pertanyaan yang tidak dijawab dengan baik. Mengapa perlu digunakan
Node *m_next;
untuk menunjuk ke node berikutnya dalam daftar, bukan
Node m_next;
Saya mengerti bahwa lebih baik menggunakan versi pointer; Saya tidak akan memperdebatkan fakta, tapi saya tidak tahu mengapa itu lebih baik. Saya mendapat jawaban yang tidak begitu jelas tentang bagaimana penunjuk lebih baik untuk alokasi memori, dan saya bertanya-tanya apakah ada orang di sini yang dapat membantu saya memahaminya dengan lebih baik.
c++
pointers
linked-list
m0meni.dll
sumber
sumber
Node m_next
bukan referensi ke node, itu penyimpanan untuk keseluruhanNode
itu sendiri.Jawaban:
Ini tidak hanya lebih baik, itu satu-satunya cara yang mungkin.
Jika Anda menyimpan
Node
objek di dalamnya, apa yang akan Anda lakukansizeof(Node)
? Itu akan menjadisizeof(int) + sizeof(Node)
, yang akan sama dengansizeof(int) + (sizeof(int) + sizeof(Node))
, yang akan sama dengansizeof(int) + (sizeof(int) + (sizeof(int) + sizeof(Node)))
, dll. Hingga tak terbatas.Objek seperti itu tidak mungkin ada. Tidak mungkin .
sumber
Di Jawa
menyimpan pointer ke node lain. Anda tidak punya pilihan tentang itu. Di C ++
artinya sama. Perbedaannya adalah bahwa dalam C ++ Anda sebenarnya dapat menyimpan objek sebagai lawan penunjuk ke sana. Itulah mengapa Anda harus mengatakan Anda menginginkan penunjuk. Di C ++:
artinya simpan simpul di sini (dan itu jelas tidak bisa berfungsi untuk daftar - Anda berakhir dengan struktur yang ditentukan secara rekursif).
sumber
Node
konstruktor milik anggota akan dipanggil, yang akan membuat instance baru lainnya ... dan Anda akan mendapatkan rekursi semu tanpa akhir. Ini tidak benar-benar begitu banyak masalah ukuran dalam hal benar-benar ketat dan literal, karena merupakan masalah kinerja.Node
yang benar-benar di dalam yang pertamaNode
. Jadi Anda membuat pointer, yang pada dasarnya adalah cara Java menangani objek, bukan primitif. Saat Anda memanggil metode atau membuat variabel, Java tidak menyimpan salinan objek atau bahkan objek itu sendiri; itu menyimpan referensi ke sebuah objek, yang pada dasarnya adalah sebuah penunjuk dengan sedikit sarung tangan anak-anak yang melilitnya. Inilah yang pada dasarnya dikatakan oleh kedua jawaban tersebut.C ++ bukan Java. Saat Anda menulis
di Jawa, itu sama dengan menulis
di C ++. Di Java, penunjuk bersifat implisit, di C ++ bersifat eksplisit. Jika Anda menulis
di C ++, Anda meletakkan sebuah instance
Node
di sana di dalam objek yang Anda definisikan. Itu selalu ada dan tidak dapat dihilangkan, tidak dapat dialokasikannew
dan tidak dapat dihapus. Efek ini tidak mungkin dicapai di Java, dan ini sama sekali berbeda dari apa yang dilakukan Java dengan sintaks yang sama.sumber
Anda menggunakan pointer, jika tidak kode Anda:
... akan tidak mengkompilasi, karena compiler tidak dapat menghitung ukuran
Node
. Ini karena ia bergantung pada dirinya sendiri - yang berarti kompilator tidak dapat memutuskan berapa banyak memori yang akan dikonsumsi.sumber
k == sizeof(Node)
penahanan dan tipe Anda memiliki data, ia juga harus menahannyasizeof(Node) = k + sizeof(Data) = sizeof(Node) + sizeof(Data)
dan kemudiansizeof(Node) > sizeof(Node)
.aleph_0
berfungsi. (Hanya terlalu bertele-tele :-))sizeof
adalah tipe integral unsigned, jadi ada harapan ukuran transfinite atau bahkan real. (menjadi lebih bertele-tele!: p)Node
bahkan tidak ditentukan sebelum akhir cuplikan ini, jadi Anda tidak dapat menggunakannya di dalam. Mengizinkan seseorang untuk secara implisit mendeklarasikan pointer ke kelas yang belum dideklarasikan adalah sedikit cheat yang diizinkan oleh bahasa untuk membuat struktur seperti itu mungkin, tanpa perlu secara eksplisit melemparkan pointer sepanjang waktu.The recent (
Node m_next
) harus berisi node. Itu tidak akan menunjukkannya. Dan kemudian tidak akan ada tautan elemen.sumber
Pendekatan yang Anda gambarkan tidak hanya kompatibel dengan C ++, tetapi juga dengan (kebanyakan) bahasa subset C-nya . Mempelajari cara mengembangkan daftar-tertaut gaya-C adalah cara yang baik untuk memperkenalkan diri Anda pada teknik pemrograman tingkat rendah (seperti manajemen memori manual), tetapi secara umum ini bukan praktik terbaik untuk pengembangan C ++ modern.
Di bawah ini, saya telah menerapkan empat variasi tentang cara mengelola daftar item di C ++.
raw_pointer_demo
menggunakan pendekatan yang sama seperti milik Anda - manajemen memori manual diperlukan dengan penggunaan pointer mentah. Penggunaan C ++ di sini hanya untuk gula sintaksis , dan pendekatan yang digunakan kompatibel dengan bahasa C.shared_pointer_demo
pengelolaan daftar masih dilakukan secara manual, namun pengelolaan memori dilakukan secara otomatis (tidak menggunakan raw pointer). Ini sangat mirip dengan apa yang mungkin Anda alami dengan Java.std_list_demo
menggunakanlist
wadah pustaka standar . Ini menunjukkan betapa lebih mudahnya hal-hal jika Anda mengandalkan pustaka yang ada daripada menggulirkan milik Anda sendiri.std_vector_demo
menggunakanvector
wadah pustaka standar . Ini mengelola penyimpanan daftar dalam satu alokasi memori yang berdekatan. Dengan kata lain, tidak ada petunjuk ke elemen individu. Untuk kasus tertentu yang agak ekstrem, ini mungkin menjadi sangat tidak efisien. Namun, untuk kasus umum, ini adalah praktik terbaik yang direkomendasikan untuk pengelolaan daftar di C ++ .Catatan: Dari semua ini, hanya yang
raw_pointer_demo
benar - benar mengharuskan daftar tersebut dihancurkan secara eksplisit untuk menghindari memori "bocor". Tiga metode lainnya akan secara otomatis menghancurkan daftar dan isinya ketika wadah keluar dari ruang lingkup (di akhir fungsi). Intinya adalah: C ++ mampu menjadi sangat "mirip Java" dalam hal ini - tetapi hanya jika Anda memilih untuk mengembangkan program menggunakan alat tingkat tinggi yang Anda inginkan.sumber
Node_reference
deklarasi di atas alamat salah satu perbedaan bahasa tingkat menarik yang paling antara Jawa dan C ++. Di Java, mendeklarasikan objek berjenisNode
akan secara implisit menggunakan referensi ke objek yang dialokasikan secara terpisah. Di C ++, Anda memiliki pilihan alokasi referensi (pointer) vs. langsung (stack) - jadi Anda harus menangani perbedaannya secara eksplisit. Dalam kebanyakan kasus, Anda akan menggunakan alokasi langsung, meskipun tidak untuk elemen daftar.Gambaran
Ada 2 cara untuk mereferensikan dan mengalokasikan objek di C ++, sedangkan di Java hanya ada satu cara.
Untuk menjelaskan hal ini, diagram berikut menunjukkan bagaimana objek disimpan dalam memori.
1.1 C ++ Item tanpa petunjuk
Peringatan : Sintaks C ++ yang digunakan dalam contoh ini, mirip dengan sintaks di Java. Tapi, alokasi memorinya berbeda.
1.2 Item C ++ menggunakan pointer
Jika Anda memeriksa perbedaan antara kedua cara tersebut, Anda akan melihat, bahwa pada teknik pertama, item alamat dialokasikan dalam pelanggan, sedangkan cara kedua, Anda harus membuat setiap alamat secara eksplisit.
Peringatan: Java mengalokasikan objek dalam memori seperti teknik kedua ini, tetapi sintaksnya seperti cara pertama, yang mungkin membingungkan pendatang baru untuk "C ++".
Penerapan
Jadi contoh daftar Anda bisa jadi mirip dengan contoh berikut.
Ringkasan
Karena Daftar Berantai memiliki jumlah item yang bervariasi, memori dialokasikan sesuai kebutuhan, dan, sebagaimana tersedia.
MEMPERBARUI:
Juga layak untuk disebutkan, karena @haccks berkomentar di postingannya.
Terkadang, referensi atau penunjuk objek, menunjukkan item bersarang (alias "Komposisi UML").
Dan terkadang, referensi atau penunjuk objek, menunjukkan item eksternal (alias "Agregasi UML").
Namun, item bertingkat dari kelas yang sama, tidak dapat diterapkan dengan teknik "tanpa penunjuk".
sumber
Di samping catatan, jika anggota pertama kelas atau struct adalah penunjuk berikutnya (jadi tidak ada fungsi virtual atau fitur lain dari kelas yang berarti next bukan anggota pertama kelas atau struct), maka Anda dapat menggunakan kelas atau struktur "dasar" hanya dengan penunjuk berikutnya, dan menggunakan kode umum untuk operasi daftar tertaut dasar seperti menambahkan, menyisipkan sebelumnya, mengambil dari depan, .... Ini karena C / C ++ menjamin bahwa alamat anggota pertama kelas atau struktur sama dengan alamat kelas atau struktur tersebut. Kelas atau struct node dasar hanya akan memiliki pointer berikutnya untuk digunakan oleh fungsi daftar tertaut dasar, kemudian typecasting akan digunakan sesuai kebutuhan untuk mengubah antara jenis node dasar dan jenis node "turunan". Catatan samping - dalam C ++, jika kelas simpul dasar hanya memiliki penunjuk berikutnya,
sumber
Alasannya adalah ketika Anda membuat sebuah
Node
objek, compiler harus mengalokasikan memori untuk objek tersebut dan untuk itu ukuran objek akan dihitung.Ukuran pointer ke tipe apa pun diketahui kompiler dan oleh karena itu dengan ukuran pointer referensi sendiri objek dapat dihitung.
Jika
Node m_node
digunakan, maka compiler tidak tahu tentang ukuranNode
dan akan terjebak dalam rekursi penghitungan yang tak terbatassizeof(Node)
. Selalu ingat: kelas tidak dapat berisi anggota dari tipenya sendiri .sumber
Karena ini di C ++
setara dengan ini di Jawa
di mana keduanya membuat objek baru
MyClass
menggunakan konstruktor default.sumber
Tentu ada jawaban yang sepele.
Jika mereka tidak menautkan satu node ke node berikutnya dengan sebuah pointer, mereka tidak akan ditautkan ke daftar .
Keberadaan daftar tertaut sebagai suatu hal karena kami ingin dapat menghubungkan objek bersama. Misalnya: kita sudah memiliki objek dari suatu tempat. Kami sekarang ingin meletakkan objek sebenarnya (bukan salinan) di akhir antrian, misalnya. Itu dicapai dengan menambahkan tautan dari elemen terakhir yang sudah ada di antrian ke entri yang kami tambahkan. Dalam istilah mesin, itu mengisi kata dengan alamat elemen berikutnya.
sumber