Saya tahu referensi adalah gula sintaksis, jadi kode lebih mudah dibaca dan ditulis.
Tapi apa perbedaannya?
int &x = *(int*)0;
pada gcc. Referensi memang dapat menunjuk ke NULL.
Saya tahu referensi adalah gula sintaksis, jadi kode lebih mudah dibaca dan ditulis.
Tapi apa perbedaannya?
int &x = *(int*)0;
pada gcc. Referensi memang dapat menunjuk ke NULL.
Jawaban:
Pointer dapat ditugaskan kembali:
int x = 5;
int y = 6;
int *p;
p = &x;
p = &y;
*p = 10;
assert(x == 5);
assert(y == 10);
Referensi tidak dapat, dan harus ditetapkan pada inisialisasi:
int x = 5;
int y = 6;
int &r = x;
Pointer memiliki alamat dan ukuran memori sendiri pada stack (4 byte pada x86), sedangkan referensi berbagi alamat memori yang sama (dengan variabel asli) tetapi juga membutuhkan ruang pada stack. Karena referensi memiliki alamat yang sama dengan variabel asli itu sendiri, aman untuk menganggap referensi sebagai nama lain untuk variabel yang sama. Catatan: Poin penunjuk bisa berada di tumpukan atau tumpukan. Ditto referensi. Klaim saya dalam pernyataan ini bukanlah bahwa pointer harus mengarah ke stack. Pointer hanyalah variabel yang menyimpan alamat memori. Variabel ini ada di tumpukan. Karena referensi memiliki ruang sendiri di stack, dan karena alamatnya sama dengan variabel, referensi. Lebih lanjut tentang tumpukan vs tumpukan. Ini menyiratkan bahwa ada alamat asli dari referensi bahwa kompiler tidak akan memberi tahu Anda.
int x = 0;
int &r = x;
int *p = &x;
int *p2 = &r;
assert(p == p2);
Anda dapat memiliki petunjuk ke petunjuk ke petunjuk yang menawarkan tingkat tipuan ekstra. Padahal referensi hanya menawarkan satu tingkat tipuan.
int x = 0;
int y = 0;
int *p = &x;
int *q = &y;
int **pp = &p;
pp = &q;//*pp = q
**pp = 4;
assert(y == 4);
assert(x == 0);
Pointer dapat ditugaskan nullptr
secara langsung, sedangkan referensi tidak bisa. Jika Anda berusaha cukup keras, dan Anda tahu caranya, Anda dapat membuat alamat referensi nullptr
. Demikian juga, jika Anda mencoba cukup keras, Anda dapat memiliki referensi ke sebuah pointer, dan kemudian referensi itu dapat berisi nullptr
.
int *p = nullptr;
int &r = nullptr; <--- compiling error
int &r = *p; <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
Pointer dapat beralih pada array; Anda dapat menggunakan ++
untuk pergi ke item berikutnya yang menunjuk ke pointer, dan + 4
untuk pergi ke elemen ke-5. Ini tidak masalah ukuran objek apa yang ditunjuk oleh pointer.
Pointer perlu *
direferensikan untuk mengakses lokasi memori yang ditunjuknya, sedangkan referensi dapat digunakan secara langsung. Pointer ke kelas / struct digunakan ->
untuk mengakses anggota itu sedangkan referensi menggunakan a .
.
Referensi tidak dapat dimasukkan ke dalam array, sedangkan pointer bisa (Disebutkan oleh pengguna @litb)
Referensi Const dapat terikat pada temporal. Pointer tidak dapat (bukan tanpa tipuan):
const int &x = int(12); //legal C++
int *y = &int(12); //illegal to dereference a temporary.
Ini membuat const&
lebih aman untuk digunakan dalam daftar argumen dan sebagainya.
Sebuah referensi dapat dianggap sebagai pointer konstan (tidak harus bingung dengan pointer ke nilai konstan!) Dengan tipuan otomatis, yaitu compiler akan menerapkan *
operator yang untuk Anda.
Semua referensi harus diinisialisasi dengan nilai bukan nol atau kompilasi akan gagal. Tidak mungkin untuk mendapatkan alamat referensi - operator alamat akan mengembalikan alamat nilai referensi sebagai gantinya - juga tidak mungkin untuk melakukan aritmatika pada referensi.
Pemrogram C mungkin tidak menyukai referensi C ++ karena tidak akan lagi terlihat ketika tipuan terjadi atau jika suatu argumen dilewatkan oleh nilai atau dengan pointer tanpa melihat tanda tangan fungsi.
Pemrogram C ++ mungkin tidak suka menggunakan pointer karena mereka dianggap tidak aman - meskipun referensi tidak benar-benar lebih aman daripada pointer konstan kecuali dalam kasus yang paling sepele - kurang kenyamanan dari tipuan otomatis dan membawa konotasi semantik yang berbeda.
Pertimbangkan pernyataan berikut dari C ++ FAQ :
Meskipun referensi sering diimplementasikan menggunakan alamat dalam bahasa assembly yang mendasari, jangan tidak memikirkan referensi sebagai mencari pointer lucu untuk sebuah objek. Referensi adalah objek. Ini bukan penunjuk ke objek, atau salinan dari objek. Itu adalah objeknya.
Tetapi jika referensi benar - benar objek, bagaimana mungkin ada referensi yang menggantung? Dalam bahasa yang tidak dikelola, tidak mungkin bagi referensi untuk menjadi lebih 'aman' daripada pointer - umumnya tidak ada cara untuk secara andal alias nilai melintasi batas cakupan!
Berasal dari latar belakang C, referensi C ++ mungkin terlihat seperti konsep yang agak konyol, tetapi orang harus tetap menggunakannya alih-alih dengan petunjuk jika memungkinkan: Ketidaktepatan otomatis menjadi nyaman, dan referensi menjadi sangat berguna ketika berhadapan dengan RAII - tetapi bukan karena adanya persepsi keselamatan keuntungan, tetapi lebih karena mereka membuat penulisan kode idiomatis kurang canggung.
RAII adalah salah satu konsep sentral C ++, tetapi berinteraksi non-sepele dengan menyalin semantik. Melewati objek dengan referensi menghindari masalah ini karena tidak ada penyalinan yang terlibat. Jika referensi tidak ada dalam bahasa, Anda harus menggunakan pointer sebagai gantinya, yang lebih rumit untuk digunakan, sehingga melanggar prinsip desain bahasa bahwa solusi praktik terbaik harus lebih mudah daripada alternatif.
Jika Anda ingin benar-benar bertele-tele, ada satu hal yang dapat Anda lakukan dengan referensi yang tidak dapat Anda lakukan dengan pointer: memperpanjang umur objek sementara. Dalam C ++ jika Anda mengikat referensi const ke objek sementara, masa pakai objek itu menjadi umur referensi.
std::string s1 = "123";
std::string s2 = "456";
std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;
Dalam contoh ini s3_copy menyalin objek sementara yang merupakan hasil dari penggabungan. Sedangkan s3_referensi pada intinya menjadi objek sementara. Ini benar-benar referensi ke objek sementara yang sekarang memiliki umur yang sama dengan referensi.
Jika Anda mencoba ini tanpa const
harus gagal untuk dikompilasi. Anda tidak dapat mengikat referensi non-const ke objek sementara, Anda juga tidak dapat mengambil alamatnya untuk hal tersebut.
const &
mengikat, dan hanya ketika referensi keluar dari ruang lingkup destruktor dari tipe referensi aktual (dibandingkan dengan tipe referensi, yang bisa menjadi basis) disebut. Karena ini adalah referensi, tidak ada pemotongan yang terjadi di antara keduanya.
Animal x = fast ? getHare() : getTortoise()
maka x
akan menghadapi masalah mengiris klasik, sementara Animal& x = ...
akan bekerja dengan benar.
Terlepas dari gula sintaksis, referensi adalah const
penunjuk ( bukan penunjuk ke a const
). Anda harus menetapkan apa yang dirujuk ketika Anda mendeklarasikan variabel referensi, dan Anda tidak dapat mengubahnya nanti.
Perbarui: sekarang saya memikirkannya lagi, ada perbedaan penting.
Target const pointer dapat diganti dengan mengambil alamatnya dan menggunakan cast const.
Sasaran referensi tidak dapat diganti dengan cara apa pun selain dari UB.
Ini harus memungkinkan kompiler untuk melakukan lebih banyak optimasi pada referensi.
T* const
dengan gula sintaksis yang berbeda (yang terjadi untuk menghilangkan banyak * dan & dari kode Anda).
int i; int const *pci = &i; /* implicit conv to const int* */ int *pi = const_cast<int*>(pci);
OK.
Berlawanan dengan pendapat umum, dimungkinkan untuk memiliki referensi yang NULL.
int * p = NULL;
int & r = *p;
r = 1; // crash! (if you're lucky)
Memang, itu jauh lebih sulit untuk dilakukan dengan referensi - tetapi jika Anda mengelolanya, Anda akan merobek rambut Anda berusaha menemukannya. Referensi secara inheren tidak aman di C ++!
Secara teknis ini adalah referensi yang tidak valid , bukan referensi nol. C ++ tidak mendukung referensi nol sebagai konsep yang mungkin Anda temukan dalam bahasa lain. Ada beberapa jenis referensi yang tidak valid juga. Setiap referensi yang tidak valid menimbulkan momok perilaku tidak terdefinisi , seperti halnya menggunakan pointer yang tidak valid.
Kesalahan aktual adalah dalam referensi dari pointer NULL, sebelum penugasan ke referensi. Tapi saya tidak mengetahui adanya kompiler yang akan menghasilkan kesalahan pada kondisi itu - kesalahan menyebar ke titik lebih jauh dalam kode. Itulah yang membuat masalah ini begitu berbahaya. Sebagian besar waktu, jika Anda mensetere pointer NULL, Anda crash tepat di tempat itu dan tidak perlu banyak debugging untuk mengetahuinya.
Contoh saya di atas pendek dan dibuat-buat. Ini contoh dunia nyata.
class MyClass
{
...
virtual void DoSomething(int,int,int,int,int);
};
void Foo(const MyClass & bar)
{
...
bar.DoSomething(i1,i2,i3,i4,i5); // crash occurs here due to memory access violation - obvious why?
}
MyClass * GetInstance()
{
if (somecondition)
return NULL;
...
}
MyClass * p = GetInstance();
Foo(*p);
Saya ingin menegaskan kembali bahwa satu-satunya cara untuk mendapatkan referensi nol adalah melalui kode salah bentuk, dan sekali Anda memilikinya Anda mendapatkan perilaku yang tidak terdefinisi. Ini tidak pernah masuk akal untuk memeriksa referensi null; misalnya Anda dapat mencoba if(&bar==NULL)...
tetapi kompilator mungkin mengoptimalkan pernyataan tidak ada! Referensi yang valid tidak akan pernah NULL sehingga dari pandangan kompiler perbandingan selalu salah, dan bebas untuk menghilangkan if
klausa sebagai kode mati - ini adalah inti dari perilaku yang tidak terdefinisi.
Cara yang tepat untuk menghindari masalah adalah dengan menghindari penereferensian pointer NULL untuk membuat referensi. Inilah cara otomatis untuk melakukannya.
template<typename T>
T& deref(T* p)
{
if (p == NULL)
throw std::invalid_argument(std::string("NULL reference"));
return *p;
}
MyClass * p = GetInstance();
Foo(deref(p));
Untuk melihat lebih lama masalah ini dari seseorang dengan keterampilan menulis yang lebih baik, lihat Referensi Null dari Jim Hyslop dan Herb Sutter.
Untuk contoh lain tentang bahaya penereferensian penunjuk nol, lihat Mengekspos perilaku tidak terdefinisi saat mencoba mengirim kode ke platform lain oleh Raymond Chen.
Anda lupa bagian terpenting:
akses anggota dengan pointer menggunakan ->
akses anggota dengan penggunaan referensi.
foo.bar
adalah jelas lebih unggul foo->bar
dalam cara yang sama bahwa vi adalah jelas lebih unggul Emacs :-)
->
dirujuk oleh pointer memiliki anggota, dan akses ke mereka itulah yang menyediakan referensi ke pointer, seperti halnya pointer itu sendiri.
.
dan ->
ada hubungannya dengan vi dan emacs :)
.
lebih baik daripada menggunakan ->
, tetapi sama seperti vi vs emacs, itu sepenuhnya subjektif dan Anda tidak dapat membuktikan apa pun
Referensi sangat mirip dengan pointer, tetapi mereka secara khusus dibuat untuk membantu mengoptimalkan kompiler.
Sebagai contoh:
void maybeModify(int& x); // may modify x in some way
void hurtTheCompilersOptimizer(short size, int array[])
{
// This function is designed to do something particularly troublesome
// for optimizers. It will constantly call maybeModify on array[0] while
// adding array[1] to array[2]..array[size-1]. There's no real reason to
// do this, other than to demonstrate the power of references.
for (int i = 2; i < (int)size; i++) {
maybeModify(array[0]);
array[i] += array[1];
}
}
Kompiler pengoptimal mungkin menyadari bahwa kita sedang mengakses cukup banyak [0] dan [1]. Alangkah baiknya untuk mengoptimalkan algoritme untuk:
void hurtTheCompilersOptimizer(short size, int array[])
{
// Do the same thing as above, but instead of accessing array[1]
// all the time, access it once and store the result in a register,
// which is much faster to do arithmetic with.
register int a0 = a[0];
register int a1 = a[1]; // access a[1] once
for (int i = 2; i < (int)size; i++) {
maybeModify(a0); // Give maybeModify a reference to a register
array[i] += a1; // Use the saved register value over and over
}
a[0] = a0; // Store the modified a[0] back into the array
}
Untuk membuat optimasi seperti itu, perlu membuktikan bahwa tidak ada yang dapat mengubah array [1] selama panggilan. Ini agak mudah dilakukan. i tidak pernah kurang dari 2, jadi array [i] tidak pernah bisa merujuk ke array [1]. maybeModify () diberikan a0 sebagai referensi (aliasing array [0]). Karena tidak ada aritmatika "referensi", kompiler hanya perlu membuktikan bahwa mungkinModify tidak pernah mendapatkan alamat x, dan telah membuktikan bahwa tidak ada yang mengubah array [1].
Itu juga harus membuktikan bahwa tidak ada cara panggilan masa depan dapat membaca / menulis [0] sementara kami memiliki salinan register sementara di a0. Ini sering sepele untuk dibuktikan, karena dalam banyak kasus jelas bahwa referensi tidak pernah disimpan dalam struktur permanen seperti instance kelas.
Sekarang lakukan hal yang sama dengan pointer
void maybeModify(int* x); // May modify x in some way
void hurtTheCompilersOptimizer(short size, int array[])
{
// Same operation, only now with pointers, making the
// optimization trickier.
for (int i = 2; i < (int)size; i++) {
maybeModify(&(array[0]));
array[i] += array[1];
}
}
Tingkah lakunya sama; hanya sekarang ini jauh lebih sulit untuk membuktikan bahwa mungkinModify tidak pernah memodifikasi array [1], karena kami telah memberikannya sebuah pointer; Kucing itu diluar kantung. Sekarang ia harus melakukan bukti yang jauh lebih sulit: analisis statis mungkinModify untuk membuktikannya tidak pernah menulis ke & x + 1. Ia juga harus membuktikan bahwa itu tidak pernah menyelamatkan pointer yang dapat merujuk ke array [0], yang hanya rumit.
Kompiler modern semakin baik dan lebih baik dalam analisis statis, tetapi selalu menyenangkan untuk membantu mereka dan menggunakan referensi.
Tentu saja, kecuali optimasi cerdas, kompiler memang akan mengubah referensi menjadi petunjuk ketika dibutuhkan.
EDIT: Lima tahun setelah memposting jawaban ini, saya menemukan perbedaan teknis yang sebenarnya di mana referensi berbeda dari hanya cara berbeda dalam memandang konsep pengalamatan yang sama. Referensi dapat memodifikasi umur objek sementara dengan cara yang pointer tidak bisa.
F createF(int argument);
void extending()
{
const F& ref = createF(5);
std::cout << ref.getArgument() << std::endl;
};
Biasanya objek sementara seperti yang dibuat oleh panggilan untuk createF(5)
dihancurkan pada akhir ekspresi. Namun, dengan mengikat objek itu ke referensi,, ref
C ++ akan memperpanjang umur objek sementara itu sampai ref
keluar dari ruang lingkup.
maybeModify
tidak mengambil alamat yang terkait dengan hal x
ini jauh lebih mudah daripada membuktikan bahwa sekelompok aritmatika pointer tidak terjadi.
void maybeModify(int& x) { 1[&x]++; }
, yang dibahas oleh komentar lain di atas
Sebenarnya, referensi tidak benar-benar seperti pointer.
Kompiler menyimpan "referensi" ke variabel, mengaitkan nama dengan alamat memori; itu tugasnya untuk menerjemahkan nama variabel apa saja ke alamat memori saat kompilasi.
Saat Anda membuat referensi, Anda hanya memberi tahu kompiler bahwa Anda menetapkan nama lain ke variabel pointer; itu sebabnya referensi tidak dapat "menunjuk ke nol", karena variabel tidak bisa, dan tidak bisa.
Pointer adalah variabel; mereka berisi alamat beberapa variabel lain, atau bisa nol. Yang penting adalah bahwa pointer memiliki nilai, sedangkan referensi hanya memiliki variabel yang direferensikan.
Sekarang beberapa penjelasan tentang kode asli:
int a = 0;
int& b = a;
Di sini Anda tidak membuat variabel lain yang menunjuk ke a
; Anda hanya menambahkan nama lain ke konten memori yang memegang nilai a
. Memori ini sekarang memiliki dua nama, a
dan b
, dan dapat diatasi menggunakan salah satu nama.
void increment(int& n)
{
n = n + 1;
}
int a;
increment(a);
Saat memanggil fungsi, kompiler biasanya menghasilkan ruang memori untuk argumen yang akan disalin. Fungsi tanda tangan mendefinisikan ruang yang harus dibuat dan memberikan nama yang harus digunakan untuk ruang ini. Mendeklarasikan parameter sebagai referensi hanya memberi tahu kompiler untuk menggunakan ruang memori variabel input alih-alih mengalokasikan ruang memori baru selama pemanggilan metode. Mungkin aneh untuk mengatakan bahwa fungsi Anda akan secara langsung memanipulasi variabel yang dideklarasikan dalam lingkup panggilan, tetapi ingat bahwa ketika menjalankan kode yang dikompilasi, tidak ada lagi ruang lingkup; hanya ada memori datar, dan kode fungsi Anda dapat memanipulasi variabel apa pun.
Sekarang mungkin ada beberapa kasus di mana kompiler Anda mungkin tidak dapat mengetahui referensi saat kompilasi, seperti ketika menggunakan variabel extern. Jadi referensi mungkin atau mungkin tidak diterapkan sebagai penunjuk dalam kode yang mendasarinya. Tetapi dalam contoh yang saya berikan kepada Anda, kemungkinan besar tidak akan diimplementasikan dengan pointer.
Referensi tidak pernah bisa NULL
.
void Foo::bar() { virtual_baz(); }
segfaults. Jika Anda tidak mengetahui bahwa referensi mungkin nol, Anda tidak dapat melacak nol kembali ke asalnya.
int &r=*p;
adalah perilaku yang tidak terdefinisi. Pada saat itu, Anda tidak memiliki "referensi yang menunjuk ke NULL," Anda memiliki program yang tidak lagi dapat dipertimbangkan sama sekali .
Sementara kedua referensi dan pointer digunakan untuk mengakses nilai lain secara tidak langsung, ada dua perbedaan penting antara referensi dan pointer. Yang pertama adalah bahwa referensi selalu merujuk ke objek: Ini adalah kesalahan untuk menentukan referensi tanpa menginisialisasi itu. Perilaku penugasan adalah perbedaan penting kedua: Menugaskan referensi mengubah objek yang menjadi acuan referensi; itu tidak mengulang referensi ke objek lain. Setelah diinisialisasi, referensi selalu merujuk ke objek dasar yang sama.
Pertimbangkan dua fragmen program ini. Yang pertama, kami menetapkan satu pointer ke yang lain:
int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2; // pi now points to ival2
Setelah penugasan, ival, objek yang ditangani oleh pi tetap tidak berubah. Tugas mengubah nilai pi, menjadikannya menunjuk ke objek yang berbeda. Sekarang pertimbangkan program serupa yang memberikan dua referensi:
int &ri = ival, &ri2 = ival2;
ri = ri2; // assigns ival2 to ival
Tugas ini mengubah ival, nilai yang dirujuk oleh ri, dan bukan referensi itu sendiri. Setelah penugasan, kedua referensi masih merujuk ke objek aslinya, dan nilai dari objek tersebut sekarang sama juga.
Ada perbedaan semantik yang mungkin tampak esoteris jika Anda tidak terbiasa mempelajari bahasa komputer secara abstrak atau bahkan mode akademik.
Pada level tertinggi, gagasan referensi adalah bahwa mereka transparan "alias". Komputer Anda mungkin menggunakan alamat untuk membuatnya berfungsi, tetapi Anda tidak perlu khawatir tentang itu: Anda seharusnya menganggapnya sebagai "hanya nama lain" untuk objek yang ada dan sintaksinya mencerminkan hal itu. Mereka lebih ketat daripada pointer sehingga kompiler Anda dapat lebih andal memperingatkan Anda ketika Anda akan membuat referensi menggantung, daripada ketika Anda akan membuat pointer menggantung.
Selain itu, tentu saja ada beberapa perbedaan praktis antara petunjuk dan referensi. Sintaks untuk menggunakannya jelas berbeda, dan Anda tidak bisa "duduk kembali" referensi, memiliki referensi ke ketiadaan, atau memiliki pointer ke referensi.
Referensi adalah alias untuk variabel lain sedangkan pointer menyimpan alamat memori variabel. Referensi umumnya digunakan sebagai parameter fungsi sehingga objek yang dikirimkan bukan salinan tetapi objek itu sendiri.
void fun(int &a, int &b); // A common usage of references.
int a = 0;
int &b = a; // b is an alias for a. Not so common to use.
Tidak masalah berapa banyak ruang yang dibutuhkan karena Anda tidak dapat benar-benar melihat efek samping (tanpa mengeksekusi kode) dari ruang apa pun yang diperlukan.
Di sisi lain, satu perbedaan utama antara referensi dan pointer adalah bahwa temporaries ditugaskan untuk referensi const hidup sampai referensi const keluar dari ruang lingkup.
Sebagai contoh:
class scope_test
{
public:
~scope_test() { printf("scope_test done!\n"); }
};
...
{
const scope_test &test= scope_test();
printf("in scope\n");
}
akan dicetak:
in scope
scope_test done!
Ini adalah mekanisme bahasa yang memungkinkan ScopeGuard berfungsi.
Ini didasarkan pada tutorial . Apa yang tertulis membuatnya lebih jelas:
>>> The address that locates a variable within memory is
what we call a reference to that variable. (5th paragraph at page 63)
>>> The variable that stores the reference to another
variable is what we call a pointer. (3rd paragraph at page 64)
Hanya untuk mengingat itu,
>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)
Terlebih lagi, karena kita bisa merujuk ke hampir semua tutorial pointer, pointer adalah objek yang didukung oleh aritmatika pointer yang membuat pointer mirip dengan array.
Lihatlah pernyataan berikut,
int Tom(0);
int & alias_Tom = Tom;
alias_Tom
dapat dipahami sebagai alias of a variable
(berbeda dengan typedef
, yaitu alias of a type
) Tom
. Tidak apa-apa untuk melupakan terminologi pernyataan tersebut untuk membuat referensi Tom
.
nullptr
? Apakah Anda benar-benar membaca bagian lain dari utas ini, atau ...?
Referensi bukan nama lain yang diberikan untuk sebagian memori. Ini adalah pointer yang tidak dapat diubah yang secara otomatis di-de-referensikan pada penggunaan. Pada dasarnya bermuara pada:
int& j = i;
Secara internal menjadi
int* const j = &i;
const
pointer. Fleksibilitas itu tidak membuktikan bahwa ada perbedaan antara referensi dan pointer.
Apa referensi di C ++? Beberapa contoh spesifik dari tipe yang bukan tipe objek .
Apa itu pointer di C ++? Beberapa contoh spesifik dari tipe yang merupakan tipe objek .
Dari definisi tipe objek ISO C ++ :
Sebuah objek jenis adalah (mungkin cv -qualified) jenis yang bukan tipe fungsi, bukan tipe referensi, dan tidak cv batal.
Mungkin penting untuk mengetahui, tipe objek adalah kategori level-atas dari semesta tipe dalam C ++. Referensi juga merupakan kategori tingkat atas. Tapi pointer tidak.
Pointer dan referensi disebutkan bersama dalam konteks tipe majemuk . Ini pada dasarnya karena sifat sintaks deklarator yang diwarisi dari (dan diperluas) C, yang tidak memiliki referensi. (Selain itu, ada lebih dari satu jenis deklarator referensi sejak C ++ 11, sementara pointer masih "tidak tergabung": &
+ &&
vs *
..) Jadi penyusunan bahasa yang spesifik dengan "ekstensi" dengan gaya C yang serupa dalam konteks ini agak masuk akal . (Saya masih akan berpendapat bahwa sintaks declarators limbah yang ekspresif sintaksis banyak , membuat kedua pengguna manusia dan implementasi frustasi. Dengan demikian, semua dari mereka tidak memenuhi syarat untuk built-indalam desain bahasa baru. Ini adalah topik yang sama sekali berbeda tentang desain PL.)
Kalau tidak, itu tidak signifikan bahwa pointer dapat dikualifikasikan sebagai jenis jenis tertentu dengan referensi bersama. Mereka hanya berbagi terlalu sedikit properti umum selain kesamaan sintaksis, sehingga tidak perlu menyatukannya dalam banyak kasus.
Perhatikan pernyataan di atas hanya menyebutkan "pointer" dan "referensi" sebagai tipe. Ada beberapa pertanyaan yang tertarik tentang instance mereka (seperti variabel). Ada juga banyak kesalahpahaman.
Perbedaan kategori tingkat atas sudah dapat mengungkapkan banyak perbedaan nyata yang tidak terkait langsung dengan petunjuk:
cv
kualifikasi tingkat atas . Referensi tidak bisa.Beberapa aturan khusus tentang referensi:
&&
parameter (sebagai "referensi penerusan") berdasarkan pada runtuhan referensi selama pengurangan parameter templat memungkinkan "penerusan yang sempurna" dari parameter.std::initializer_list
mengikuti beberapa aturan serupa perpanjangan seumur hidup referensi. Ini adalah kaleng cacing.Saya tahu referensi adalah gula sintaksis, jadi kode lebih mudah dibaca dan ditulis.
Secara teknis, ini salah. Referensi bukan gula sintaksis dari fitur lain di C ++, karena mereka tidak dapat secara tepat digantikan oleh fitur lain tanpa perbedaan semantik.
(Demikian pula, lambda ekspresi s yang tidak sintaksis gula dari setiap fitur lain dalam C ++ karena tidak dapat tepat disimulasikan dengan "tidak ditentukan" properti seperti urutan deklarasi variabel ditangkap , yang mungkin penting karena urutan inisialisasi variabel tersebut dapat penting.)
C ++ hanya memiliki beberapa jenis gula sintaksis dalam arti yang ketat ini. Salah satu contoh adalah (diwarisi dari C) operator built-in (non-overloaded) []
, yang didefinisikan secara tepat memiliki sifat semantik yang sama dari bentuk kombinasi tertentu dibandingkan operator built-in unary *
dan binary+
.
Jadi, pointer dan referensi keduanya menggunakan jumlah memori yang sama.
Pernyataan di atas benar-benar salah. Untuk menghindari kesalahpahaman seperti itu, lihat aturan ISO C ++ sebagai gantinya:
Dari [intro.object] / 1 :
... Objek menempati wilayah penyimpanan dalam periode konstruksinya, sepanjang masa pakainya, dan dalam periode kehancurannya. ...
Dari [dcl.ref] / 4 :
Tidak ditentukan apakah referensi membutuhkan penyimpanan atau tidak.
Perhatikan ini adalah properti semantik .
Meskipun pointer tidak cukup memenuhi syarat untuk disatukan dengan referensi dalam arti desain bahasa, masih ada beberapa argumen yang membuatnya diperdebatkan untuk membuat pilihan di antara mereka dalam beberapa konteks lain, misalnya, ketika membuat pilihan pada tipe parameter.
Tapi ini bukan keseluruhan cerita. Maksud saya, ada lebih banyak hal daripada petunjuk vs referensi yang harus Anda pertimbangkan.
Jika Anda tidak harus tetap pada pilihan yang terlalu spesifik, dalam kebanyakan kasus jawabannya pendek: Anda tidak memiliki keharusan untuk menggunakan pointer, jadi Anda tidak perlu . Pointer biasanya cukup buruk karena mereka menyiratkan terlalu banyak hal yang tidak Anda harapkan dan mereka akan bergantung pada terlalu banyak asumsi implisit yang merusak pemeliharaan dan (bahkan) portabilitas kode. Mengandalkan pointer secara tidak perlu jelas merupakan gaya yang buruk dan harus dihindari dalam pengertian C ++ modern. Pertimbangkan kembali tujuan Anda dan akhirnya Anda akan menemukan bahwa pointer adalah fitur yang paling terakhir dalam banyak kasus.
&
tipe referensi sebagai jenis parameter 1. (Dan biasanya itu harus const
memenuhi syarat.)&&
tipe referensi sebagai jenis parameter 1. (Dan biasanya seharusnya tidak ada kualifikasi.)operator=
sebagai fungsi anggota khusus membutuhkan tipe referensi yang mirip dengan parameter pertama copy / move constructor.++
membutuhkan dummy int
.unique_ptr
dan shared_ptr
(atau bahkan dengan handebrew sendiri jika Anda mengharuskannya buram ), daripada pointer mentah.std::optional
, daripada pointer mentah.observer_ptr
di Library Fundamental TS.Satu-satunya pengecualian tidak dapat dikerjakan dalam bahasa saat ini:
operator new
. (Namun, cv - void*
masih sangat berbeda dan lebih aman dibandingkan dengan pointer objek biasa karena mengesampingkan aritmetik pointer yang tidak terduga kecuali Anda mengandalkan ekstensi yang tidak sesuai pada void*
GNU.)Jadi, dalam praktiknya, jawabannya sangat jelas: ketika ragu, hindari petunjuk . Anda harus menggunakan pointer hanya ketika ada alasan yang sangat eksplisit bahwa tidak ada hal lain yang lebih tepat. Kecuali beberapa kasus luar biasa yang disebutkan di atas, pilihan seperti itu hampir selalu tidak murni C ++ - spesifik (tetapi cenderung spesifik implementasi bahasa). Contoh seperti itu dapat:
Jika Anda datang untuk melihat pertanyaan melalui beberapa hasil pencarian Google (tidak spesifik untuk C ++) , ini kemungkinan besar merupakan tempat yang salah.
Referensi dalam C ++ cukup "aneh", karena pada dasarnya tidak kelas: mereka akan diperlakukan sebagai objek atau fungsi yang dimaksud sehingga mereka tidak memiliki kesempatan untuk mendukung beberapa operasi kelas seperti berada operan kiri yang operator akses anggota secara independen ke jenis objek yang dimaksud. Bahasa lain mungkin atau mungkin tidak memiliki batasan serupa pada referensi mereka.
Referensi dalam C ++ kemungkinan tidak akan mempertahankan makna di berbagai bahasa. Misalnya, referensi secara umum tidak menyiratkan properti nonnull pada nilai-nilai seperti mereka di C ++, jadi asumsi seperti itu mungkin tidak berfungsi dalam beberapa bahasa lain (dan Anda akan menemukan contoh tandingan cukup mudah, misalnya Java, C #, ...).
Masih ada beberapa sifat umum di antara referensi dalam berbagai bahasa pemrograman pada umumnya, tetapi mari kita tinggalkan untuk beberapa pertanyaan lain di SO.
(Catatan tambahan: pertanyaannya mungkin lebih penting daripada bahasa "C-like" apa pun yang terlibat, seperti ALGOL 68 vs. PL / I. )
Referensi ke sebuah pointer dimungkinkan dalam C ++, tetapi sebaliknya tidak mungkin berarti pointer ke sebuah referensi tidak mungkin. Referensi ke sebuah pointer menyediakan sintaks yang lebih bersih untuk memodifikasi pointer. Lihat contoh ini:
#include<iostream>
using namespace std;
void swap(char * &str1, char * &str2)
{
char *temp = str1;
str1 = str2;
str2 = temp;
}
int main()
{
char *str1 = "Hi";
char *str2 = "Hello";
swap(str1, str2);
cout<<"str1 is "<<str1<<endl;
cout<<"str2 is "<<str2<<endl;
return 0;
}
Dan perhatikan versi C dari program di atas. Dalam C Anda harus menggunakan pointer ke pointer (beberapa tipuan), dan itu menyebabkan kebingungan dan program mungkin terlihat rumit.
#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
char *temp = *str1_ptr;
*str1_ptr = *str2_ptr;
*str2_ptr = temp;
}
int main()
{
char *str1 = "Hi";
char *str2 = "Hello";
swap1(&str1, &str2);
printf("str1 is %s, str2 is %s", str1, str2);
return 0;
}
Kunjungi berikut ini untuk informasi lebih lanjut tentang referensi ke penunjuk:
Seperti yang saya katakan, pointer ke referensi tidak mungkin. Coba program berikut ini:
#include <iostream>
using namespace std;
int main()
{
int x = 10;
int *ptr = &x;
int &*ptr1 = ptr;
}
Saya menggunakan referensi kecuali saya membutuhkan salah satu dari ini:
Null pointer dapat digunakan sebagai nilai sentinel, seringkali merupakan cara murah untuk menghindari fungsi yang berlebihan atau penggunaan bool.
Anda dapat melakukan aritmatika pada sebuah pointer. Sebagai contoh,p += offset;
&r + offset
mana r
dinyatakan sebagai referensi
Ada satu perbedaan mendasar antara pointer dan referensi yang saya tidak melihat ada yang disebutkan: referensi memungkinkan semantik referensi lewat dalam argumen fungsi. Pointer, meskipun tidak terlihat pada awalnya tidak: mereka hanya menyediakan semantik nilai-demi-nilai. Ini telah dijelaskan dengan sangat baik dalam artikel ini .
Salam, & rzej
Dengan risiko menambah kebingungan, saya ingin memasukkan beberapa input, saya yakin itu sebagian besar tergantung pada bagaimana kompiler mengimplementasikan referensi, tetapi dalam kasus gcc gagasan bahwa referensi hanya dapat menunjuk ke variabel di stack sebenarnya tidak benar, ambil contoh ini:
#include <iostream>
int main(int argc, char** argv) {
// Create a string on the heap
std::string *str_ptr = new std::string("THIS IS A STRING");
// Dereference the string on the heap, and assign it to the reference
std::string &str_ref = *str_ptr;
// Not even a compiler warning! At least with gcc
// Now lets try to print it's value!
std::cout << str_ref << std::endl;
// It works! Now lets print and compare actual memory addresses
std::cout << str_ptr << " : " << &str_ref << std::endl;
// Exactly the same, now remember to free the memory on the heap
delete str_ptr;
}
Yang menghasilkan ini:
THIS IS A STRING
0xbb2070 : 0xbb2070
Jika Anda melihat bahkan alamat memori persis sama, artinya referensi berhasil menunjuk ke variabel di heap! Sekarang jika Anda benar-benar ingin menjadi aneh, ini juga berfungsi:
int main(int argc, char** argv) {
// In the actual new declaration let immediately de-reference and assign it to the reference
std::string &str_ref = *(new std::string("THIS IS A STRING"));
// Once again, it works! (at least in gcc)
std::cout << str_ref;
// Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
delete &str_ref;
/*And, it works, because we are taking the memory address that the reference is
storing, and deleting it, which is all a pointer is doing, just we have to specify
the address with '&' whereas a pointer does that implicitly, this is sort of like
calling delete &(*str_ptr); (which also compiles and runs fine).*/
}
Yang menghasilkan ini:
THIS IS A STRING
Oleh karena itu referensi ADALAH pointer di bawah kap, mereka berdua hanya menyimpan alamat memori, di mana alamat menunjuk tidak relevan, apa yang menurut Anda akan terjadi jika saya memanggil std :: cout << str_ref; SETELAH menelepon hapus & str_ref? Yah, jelas itu mengkompilasi baik-baik saja, tetapi menyebabkan kesalahan segmentasi saat runtime karena tidak lagi menunjuk pada variabel yang valid, kami pada dasarnya memiliki referensi rusak yang masih ada (sampai jatuh keluar dari ruang lingkup), tetapi tidak berguna.
Dengan kata lain, referensi tidak lain adalah sebuah pointer yang memiliki mekanisme pointer diabstraksi, membuatnya lebih aman dan lebih mudah digunakan (tidak ada matematika pointer yang tidak disengaja, tidak ada pencampuran '.' Dan '->', dll.), Dengan asumsi Anda jangan coba-coba omong kosong seperti contoh saya di atas;)
Sekarang terlepas dari bagaimana kompiler menangani referensi, ia akan selalu memiliki semacam pointer di bawah kap, karena referensi harus merujuk ke variabel tertentu pada alamat memori tertentu agar dapat berfungsi seperti yang diharapkan, tidak ada jalan untuk mengatasi hal ini (karenanya istilah 'referensi').
Satu-satunya aturan utama yang penting untuk diingat dengan referensi adalah bahwa mereka harus didefinisikan pada saat deklarasi (dengan pengecualian referensi di header, dalam hal ini harus didefinisikan dalam konstruktor, setelah objek yang dikandungnya adalah dibangun sudah terlambat untuk mendefinisikannya).
Ingat, contoh saya di atas hanya itu, contoh menunjukkan apa referensi itu, Anda tidak akan pernah ingin menggunakan referensi dengan cara itu! Untuk penggunaan referensi yang benar, ada banyak jawaban di sini yang sudah menyentuh kepala
Perbedaan lain adalah bahwa Anda dapat memiliki pointer ke tipe void (dan itu berarti pointer ke apa pun) tetapi referensi untuk void dilarang.
int a;
void * p = &a; // ok
void & p = a; // forbidden
Saya tidak bisa mengatakan saya sangat senang dengan perbedaan khusus ini. Saya lebih suka itu akan diizinkan dengan referensi makna apa pun dengan alamat dan perilaku yang sama untuk referensi. Ini akan memungkinkan untuk mendefinisikan beberapa ekuivalen fungsi perpustakaan C seperti memcpy menggunakan referensi.
Juga, referensi yang merupakan parameter untuk fungsi yang digariskan dapat ditangani secara berbeda dari pointer.
void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
int testptr=0;
increment(&testptr);
}
void increftest()
{
int testref=0;
increment(testref);
}
Banyak kompiler ketika inlining versi satu pointer benar-benar akan memaksa menulis ke memori (kami mengambil alamat secara eksplisit). Namun, mereka akan meninggalkan referensi dalam register yang lebih optimal.
Tentu saja, untuk fungsi-fungsi yang tidak digarisbawahi pointer dan referensi menghasilkan kode yang sama dan selalu lebih baik untuk lulus nilai intrinsik daripada dengan referensi jika mereka tidak dimodifikasi dan dikembalikan oleh fungsi.
Penggunaan referensi lain yang menarik adalah untuk menyediakan argumen default dari tipe yang ditentukan pengguna:
class UDT
{
public:
UDT() : val_d(33) {};
UDT(int val) : val_d(val) {};
virtual ~UDT() {};
private:
int val_d;
};
class UDT_Derived : public UDT
{
public:
UDT_Derived() : UDT() {};
virtual ~UDT_Derived() {};
};
class Behavior
{
public:
Behavior(
const UDT &udt = UDT()
) {};
};
int main()
{
Behavior b; // take default
UDT u(88);
Behavior c(u);
UDT_Derived ud;
Behavior d(ud);
return 1;
}
Rasa standar menggunakan referensi 'binding const untuk aspek referensi sementara'.
Program ini dapat membantu dalam memahami jawaban pertanyaan. Ini adalah program sederhana dari referensi "j" dan pointer "ptr" yang menunjuk ke variabel "x".
#include<iostream>
using namespace std;
int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"
cout << "x=" << x << endl;
cout << "&x=" << &x << endl;
cout << "j=" << j << endl;
cout << "&j=" << &j << endl;
cout << "*ptr=" << *ptr << endl;
cout << "ptr=" << ptr << endl;
cout << "&ptr=" << &ptr << endl;
getch();
}
Jalankan program dan lihat hasilnya dan Anda akan mengerti.
Juga, luangkan 10 menit dan tonton video ini: https://www.youtube.com/watch?v=rlJrrGV0iOg
Saya merasa masih ada hal lain yang belum dibahas di sini.
Tidak seperti pointer, referensi secara sintaksis setara dengan objek yang dirujuk, yaitu operasi apa pun yang dapat diterapkan pada objek bekerja untuk referensi, dan dengan sintaks yang sama persis (pengecualian tentu saja inisialisasi).
Meskipun ini mungkin tampak dangkal, saya percaya properti ini sangat penting untuk sejumlah fitur C ++, misalnya:
Templat . Karena parameter template diketik bebek, sifat sintaksis suatu tipe adalah yang terpenting, sehingga sering kali templat yang sama dapat digunakan dengan keduanya T
dan T&
.
(atau std::reference_wrapper<T>
yang masih bergantung pada pemeran implisit T&
)
Template yang mencakup keduanya T&
dan T&&
bahkan lebih umum.
Lvalues . Pertimbangkan pernyataan str[0] = 'X';
Tanpa referensi itu hanya akan berfungsi untuk c-string ( char* str
). Mengembalikan karakter dengan referensi memungkinkan kelas yang ditentukan pengguna memiliki notasi yang sama.
Salin konstruktor . Secara sintaksis masuk akal untuk meneruskan objek untuk menyalin konstruktor, dan bukan penunjuk ke objek. Tetapi tidak ada cara bagi copy constructor untuk mengambil objek berdasarkan nilai - itu akan menghasilkan panggilan rekursif ke copy constructor yang sama. Ini menjadikan referensi sebagai satu-satunya pilihan di sini.
Operator kelebihan beban . Dengan referensi dimungkinkan untuk memperkenalkan tipuan ke panggilan operator - katakan, operator+(const T& a, const T& b)
sambil mempertahankan notasi infiks yang sama. Ini juga berfungsi untuk fungsi kelebihan beban reguler.
Poin-poin ini memberdayakan sebagian besar C ++ dan perpustakaan standar sehingga ini adalah properti referensi yang cukup utama.
Ada perbedaan non-teknis yang sangat penting antara pointer dan referensi: Argumen yang diteruskan ke fungsi oleh pointer jauh lebih terlihat daripada argumen yang dilewatkan ke fungsi oleh referensi non-const. Sebagai contoh:
void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);
void bar() {
std::string x;
fn1(x); // Cannot modify x
fn2(x); // Cannot modify x (without const_cast)
fn3(x); // CAN modify x!
fn4(&x); // Can modify x (but is obvious about it)
}
Kembali di C, panggilan yang sepertinya fn(x)
hanya bisa diteruskan oleh nilai, jadi itu pasti tidak dapat memodifikasi x
; untuk memodifikasi argumen, Anda harus melewati sebuah pointer fn(&x)
. Jadi, jika argumen tidak diawali oleh &
Anda tahu itu tidak akan diubah. (Kebalikannya, &
berarti dimodifikasi, itu tidak benar karena Anda terkadang harus melewati struktur read-only besar dengan const
pointer.)
Beberapa berpendapat bahwa ini adalah fitur yang sangat berguna ketika membaca kode, bahwa parameter pointer harus selalu digunakan untuk parameter yang dapat dimodifikasi daripada non- const
referensi, bahkan jika fungsi tidak pernah mengharapkan a nullptr
. Artinya, orang-orang berpendapat bahwa tanda tangan fungsi seperti di fn3()
atas tidak boleh diizinkan. Pedoman gaya C ++ Google adalah contohnya.
Pointer dapat diinisialisasi ke 0 dan referensi tidak. Bahkan, referensi juga harus merujuk ke objek, tetapi sebuah pointer bisa menjadi null pointer:
int* p = 0;
Tetapi kita tidak dapat memiliki int& p = 0;
dan juga int& p=5 ;
.
Bahkan untuk melakukannya dengan benar, kita harus mendeklarasikan dan mendefinisikan objek pada awalnya, lalu kita dapat membuat referensi ke objek itu, sehingga implementasi yang benar dari kode sebelumnya adalah:
Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;
Poin penting lainnya adalah kita dapat membuat deklarasi pointer tanpa inisialisasi namun tidak ada hal seperti itu dapat dilakukan dalam kasus referensi yang harus membuat referensi selalu ke variabel atau objek. Namun penggunaan pointer seperti itu berisiko sehingga secara umum kami memeriksa apakah pointer sebenarnya menunjuk ke sesuatu atau tidak. Dalam hal referensi tidak diperlukan pemeriksaan seperti itu, karena kita sudah tahu bahwa merujuk ke objek selama deklarasi adalah wajib.
Perbedaan lain adalah bahwa pointer dapat menunjuk ke objek lain namun referensi selalu merujuk ke objek yang sama, mari kita ambil contoh ini:
Int a = 6, b = 5;
Int& rf = a;
Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.
rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased
Poin lain: Ketika kita memiliki templat seperti templat STL, templat kelas semacam itu akan selalu mengembalikan referensi, bukan penunjuk, untuk memudahkan membaca atau menetapkan nilai baru menggunakan operator []:
Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="
const int& i = 0
.
Perbedaannya adalah bahwa variabel pointer non-konstan (jangan dikacaukan dengan pointer ke konstan) dapat diubah pada suatu waktu selama pelaksanaan program, memerlukan semantik pointer untuk digunakan operator (&, *), sementara referensi dapat ditetapkan pada inisialisasi hanya (itu sebabnya Anda dapat mengaturnya dalam daftar penginisialisasi konstruktor saja, tetapi tidak entah bagaimana cara lain) dan menggunakan semantik mengakses nilai biasa. Pada dasarnya referensi diperkenalkan untuk memungkinkan dukungan bagi operator kelebihan seperti yang saya baca di beberapa buku yang sangat tua. Seperti seseorang yang dinyatakan dalam utas ini - pointer dapat diatur ke 0 atau nilai apa pun yang Anda inginkan. 0 (NULL, nullptr) berarti bahwa pointer diinisialisasi dengan nol. Ini adalah kesalahan untuk dereference null pointer. Tetapi sebenarnya pointer mungkin berisi nilai yang tidak menunjuk ke beberapa lokasi memori yang benar. Referensi pada gilirannya mencoba untuk tidak memungkinkan pengguna untuk menginisialisasi referensi ke sesuatu yang tidak dapat direferensikan karena fakta bahwa Anda selalu memberikan nilai dari jenis yang benar untuk itu. Meskipun ada banyak cara untuk membuat variabel referensi diinisialisasi ke lokasi memori yang salah - lebih baik bagi Anda untuk tidak menggali lebih dalam rincian ini. Pada level mesin, baik pointer maupun referensi bekerja secara seragam - melalui pointer. Katakanlah dalam referensi penting adalah gula sintaksis. referensi nilai berbeda untuk ini - mereka secara alami tumpukan / tumpukan objek. Meskipun ada banyak cara untuk membuat variabel referensi diinisialisasi ke lokasi memori yang salah - lebih baik bagi Anda untuk tidak menggali lebih dalam rincian ini. Pada level mesin, baik pointer maupun referensi bekerja secara seragam - melalui pointer. Katakanlah dalam referensi penting adalah gula sintaksis. referensi nilai berbeda untuk ini - mereka secara alami tumpukan / tumpukan objek. Meskipun ada banyak cara untuk membuat variabel referensi diinisialisasi ke lokasi memori yang salah - lebih baik bagi Anda untuk tidak menggali lebih dalam rincian ini. Pada level mesin, baik pointer maupun referensi bekerja secara seragam - melalui pointer. Katakanlah dalam referensi penting adalah gula sintaksis. referensi nilai berbeda untuk ini - mereka secara alami tumpukan / tumpukan objek.
dalam kata-kata sederhana, kita dapat mengatakan referensi adalah nama alternatif untuk variabel sedangkan, pointer adalah variabel yang menyimpan alamat variabel lain. misalnya
int a = 20;
int &r = a;
r = 40; /* now the value of a is changed to 40 */
int b =20;
int *ptr;
ptr = &b; /*assigns address of b to ptr not the value */