Referansların sözdizimsel şeker olduğunu biliyorum, bu yüzden kodun okunması ve yazılması daha kolay.
Peki farklılıklar nelerdir?
int &x = *(int*)0;
gcc. Referans gerçekten NULL değerine işaret edebilir.
Referansların sözdizimsel şeker olduğunu biliyorum, bu yüzden kodun okunması ve yazılması daha kolay.
Peki farklılıklar nelerdir?
int &x = *(int*)0;
gcc. Referans gerçekten NULL değerine işaret edebilir.
Yanıtlar:
Bir işaretçi yeniden atanabilir:
int x = 5;
int y = 6;
int *p;
p = &x;
p = &y;
*p = 10;
assert(x == 5);
assert(y == 10);
Bir başvuru başlatılamıyor ve başlatma sırasında atanması gerekiyor:
int x = 5;
int y = 6;
int &r = x;
Bir işaretçinin yığın üzerinde kendi bellek adresi ve boyutu vardır (x86'da 4 bayt), ancak bir başvuru aynı bellek adresini (orijinal değişkenle) paylaşır, ancak yığın üzerinde biraz yer kaplar. Bir başvuru orijinal değişkenin kendisiyle aynı adrese sahip olduğundan, aynı değişken için başka bir ad olarak bir referans düşünmek güvenlidir. Not: Yığında ya da yığın üzerinde ne işaretçi olabilir. Bir referans. Bu ifadedeki iddiam, bir işaretçinin yığına işaret etmesi gerektiği değildir. İşaretçi, bir bellek adresini tutan bir değişkendir. Bu değişken yığın üzerindedir. Bir başvurunun yığın üzerinde kendi alanı olduğundan ve adres başvurduğu değişkenle aynı olduğundan. Yığın ve yığın hakkında daha fazla bilgi. Bu, derleyicinin size söylemeyeceği bir referansın gerçek bir adresi olduğunu gösterir.
int x = 0;
int &r = x;
int *p = &x;
int *p2 = &r;
assert(p == p2);
Ekstra dolaylı seviyeler sunan işaretçilerden göstericilere işaretçileriniz olabilir. Halbuki referanslar sadece bir seviye dolaylama sunar.
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);
Bir işaretçi nullptr
doğrudan atanabilirken referans gösterilemez. Yeterince denerseniz ve nasıl yapıldığını biliyorsanız, referansın adresini yapabilirsiniz nullptr
. Benzer şekilde, yeterince denerseniz, bir işaretçiye bir referansınız olabilir ve daha sonra bu referans içerebilir 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
İşaretçiler bir dizi üzerinde yineleme yapabilir; Kullanabileceğiniz ++
bir işaretçi işaret ettiğini sonraki öğeye gitmek için ve + 4
5 element gidin. Bu, işaretçinin işaret ettiği nesnenin boyutu ne olursa olsun.
İşaretçinin işaret *
ettiği bellek konumuna erişmek için kayıttan çıkarılması gerekirken, bir referans doğrudan kullanılabilir. Bir sınıf / yapıya işaretçi ->
üyelerine erişmek için kullanır , referans ise a kullanır .
.
Referanslar bir diziye doldurulamaz, ancak işaretçiler olabilir (user @litb tarafından bahsedilir)
Const başvuruları geçicilere bağlanabilir. İşaretçiler yapamaz (dolaylı olarak değil):
const int &x = int(12); //legal C++
int *y = &int(12); //illegal to dereference a temporary.
Bu, const&
argüman listelerinde vb. Kullanım için daha güvenli hale getirir .
Bir referans , otomatik dolaylama ile sabit bir işaretçi (sabit bir değere işaretçi ile karıştırılmamalıdır!) Olarak düşünülebilir, yani derleyici *
operatörü sizin için uygulayacaktır .
Tüm referanslar null olmayan bir değerle başlatılmalıdır, aksi takdirde derleme başarısız olur. Bir referansın adresini almak mümkün değildir - adres operatörü bunun yerine referanslanan değerin adresini döndürecektir - veya referanslar üzerinde aritmetik yapmak mümkün değildir.
C programcıları, C ++ referanslarından hoşlanmayabilir, çünkü dolaylı aktarım gerçekleştiğinde veya bir bağımsız değişken değer veya işaretçi tarafından işlev imzalarına bakmadan geçirilirse artık açık olmayacaktır.
C ++ programcıları, güvenli olmayan olarak kabul edildiğinden, işaretçileri kullanmaktan hoşlanmayabilir - referanslar en önemsiz durumlar dışında sabit işaretçilerden daha güvenli olmasa da, otomatik dolaylama kolaylığından yoksundur ve farklı bir anlamsal çağrışım taşırlar.
C ++ SSS bölümündeki şu ifadeyi düşünün :
Bir referans genellikle altta yatan montaj dilde bir adres kullanılarak uygulanır rağmen lütfen değil bir nesneye komik görünümlü işaretçi olarak bir referans düşünüyorum. Bir referans olan nesne. Nesneye bir işaretçi veya nesnenin bir kopyası değildir. O ise nesne.
Fakat bir referans gerçekten nesne olsaydı , sarkan referanslar nasıl olabilir? Yönetilmeyen dillerde, referansların işaretçilerden 'daha güvenli' olması imkansızdır - genellikle kapsam sınırları boyunca değerleri güvenilir bir şekilde takma adlamanın bir yolu yoktur!
C arka plandan geliyor, C ++ referanslar biraz saçma bir kavram gibi görünebilir, ama hala mümkün işaretçiler yerine bunları kullanmak gerekir: Otomatik indirection olan kullanışlı ve ilgilenirken referanslar özellikle yararlıdır haline de ray - ancak herhangi algılanan güvenlik çünkü deyimsel kod yazmayı daha az garip hale getirdikleri için.
RAII, C ++ 'nın temel kavramlarından biridir, ancak kopyalama semantiği ile önemsiz bir şekilde etkileşime girer. Nesneleri başvuru ile iletmek, kopyalama yapılmadığı için bu sorunları önler. Dilde referanslar olmasaydı, bunun yerine daha hantal olan işaretçiler kullanmanız gerekir, böylece dil tasarımı ilkesini, en iyi uygulama çözümünün alternatiflerden daha kolay olması gerektiği ihlal edilir.
Gerçekten bilgiçlikçili olmak istiyorsanız, bir işaretçi ile yapamayacağınız bir referansla yapabileceğiniz bir şey vardır: geçici bir nesnenin ömrünü uzatın. C ++ 'da geçici bir nesneye const başvurusu bağlarsanız, o nesnenin ömrü başvurunun ömrü haline gelir.
std::string s1 = "123";
std::string s2 = "456";
std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;
Bu örnekte s3_copy, birleştirme işleminin sonucu olan geçici nesneyi kopyalar. Oysa s3_reference özünde geçici nesne olur. Gerçekten de referansla aynı kullanım ömrüne sahip geçici bir nesneye yapılan bir referans.
Bunu onsuz denerseniz const
derlemek başarısız olur. Const olmayan bir başvuruyu geçici bir nesneye bağlayamazsınız veya bu konu için adresini alamazsınız.
const &
bağlanma olgusu ile uzatılır ve yalnızca referans kapsam dışına çıktığında, gerçek referans türünün yıkıcısı ( referans türüyle karşılaştırıldığında, bu bir taban olabilir) çağrılır. Bir referans olduğundan, aralarında dilimleme yapılmayacaktır.
Animal x = fast ? getHare() : getTortoise()
o x
zaman klasik dilimleme problemi ile karşı karşıya kalırsanız, Animal& x = ...
doğru çalışır.
Sözdizimsel şeker dışında bir referans bir const
göstericidir ( a'ya değilconst
). Referans değişkeni bildirirken ne anlama geldiğini belirlemelisiniz ve daha sonra değiştiremezsiniz.
Güncelleme: Şimdi biraz daha düşündüğüme göre, önemli bir fark var.
Bir sabit işaretçinin hedefi, adresi alarak ve sabit bir döküm kullanılarak değiştirilebilir.
Bir referansın hedefi UB'den kısa bir şekilde değiştirilemez.
Bu, derleyicinin bir referans üzerinde daha fazla optimizasyon yapmasına izin vermelidir.
T* const
farklı bir sözdizimsel şeker olarak anladım (bu, kodunuzu * ve & 'dan çok fazla ortadan kaldırır).
int i; int const *pci = &i; /* implicit conv to const int* */ int *pi = const_cast<int*>(pci);
tamam.
Popüler düşüncenin aksine, NULL olan bir referansa sahip olmak mümkündür.
int * p = NULL;
int & r = *p;
r = 1; // crash! (if you're lucky)
Verilmiş, bir referansla yapmak çok daha zor - ama eğer yönetirseniz, saçınızı bulmaya çalışırken yırtacaksınız. Referanslar C ++ ' da doğal olarak güvenli değildir !
Teknik olarak bu geçersiz bir referans , boş bir referans değil. C ++, diğer dillerde bulabileceğiniz gibi bir kavram olarak null referansları desteklemez. Başka geçersiz referans türleri de vardır. Herhangi bir geçersiz başvuru , tıpkı geçersiz bir işaretçi kullanmak gibi tanımsız davranışların hayaletini yükseltir .
Asıl hata, bir referansa atamadan önce NULL işaretçisinin kaydının kaldırılmasıdır. Ama bu koşulda herhangi bir hata üretecek herhangi bir derleyici farkında değilim - hata kodda birlikte bir noktaya yayılır. Bu sorunu bu kadar sinsi yapan da budur. Çoğu zaman, bir NULL işaretçisi dereference, o noktada çöküyor ve bunu anlamak için çok hata ayıklama gerektirmez.
Yukarıdaki örneğim kısa ve tutarlı. İşte daha gerçek bir örnek.
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);
Bir null başvuru elde etmenin tek yolunun hatalı biçimlendirilmiş kod aracılığıyla olduğunu tekrarlamak istiyorum ve bir kez sahip olduğunuzda tanımsız davranış alıyorsunuz. Bu asla bir null başvuru olup olmadığını kontrol etmek mantıklı; örneğin deneyebilirsiniz, if(&bar==NULL)...
ancak derleyici ifadeyi varlığını optimize edebilir! Geçerli bir başvuru hiçbir zaman NULL olamaz, bu nedenle derleyicinin görüşüne göre karşılaştırma her zaman yanlıştır ve if
cümleyi ölü kod olarak ortadan kaldırmak ücretsizdir - bu tanımlanmamış davranışın özüdür.
Beladan uzak durmanın uygun yolu, bir NULL işaretçisinin referansı oluşturmak için kayıttan çıkarılmasını önlemektir. İşte bunu başarmanın otomatik bir yolu.
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));
Daha iyi yazma becerisine sahip birinden bu soruna daha eski bir bakış için Jim Hyslop ve Herb Sutter'den Null Referanslara bakın .
Bir boş göstergenin silinmesinin tehlikelerine başka bir örnek için bkz . Raymond Chen tarafından başka bir platforma kod aktarmaya çalışırken tanımlanmamış davranışı gösterme .
En önemli kısmı unuttun:
işaretçilerle ->
üye erişimi başvurularla üye erişimi.
foo.bar
olduğu açıkça daha üstün foo->bar
olduğu aynı şekilde vi olduğunu açık bir şekilde daha üstün Emacs :-)
->
başvurduğu nesnenin üyeleri vardır ve bunlara erişim , tıpkı işaretçinin kendisinde olduğu gibi, işaretçilere referans sağlayan şeydir .
.
ve ->
vi ve emacs ile ilgili bir şey var :)
.
daha iyi olduğunu düşünüyorum ->
, ama vi vs emacs gibi, tamamen özneldir ve hiçbir şeyi kanıtlayamazsınız
Referanslar işaretçilerle çok benzer, ancak derleyicileri optimize etmeye yardımcı olmak için özel olarak hazırlanmıştır.
Örnek olarak:
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];
}
}
Optimize edici bir derleyici, [0] ve [1] gruplarına eriştiğimizi fark edebilir. Algoritmayı şu şekilde optimize etmek isteriz:
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
}
Böyle bir optimizasyon yapmak için, arama sırasında hiçbir şeyin diziyi [1] değiştiremeyeceğini kanıtlaması gerekir. Bunu yapmak oldukça kolay. i hiçbir zaman 2'den az değildir, bu nedenle [i] dizisi hiçbir zaman [1] dizisine atıfta bulunamaz. maybeModify () öğesine başvuru olarak a0 (diğer ad dizisi [0]) verilir. "Referans" aritmetik olmadığından, derleyici sadece belkiModify'ın x adresini asla almadığını kanıtlamak zorundadır ve hiçbir şeyin diziyi değiştirmediğini kanıtlamıştır [1].
Ayrıca, gelecekteki bir çağrının [0] 'ı okuyabilmesi / yazabilmesinin hiçbir yolu olmadığını kanıtlamak zorundayız. Bu genellikle kanıtlamak için önemsizdir, çünkü birçok durumda referansın asla sınıf örneği gibi kalıcı bir yapıda saklanmadığı açıktır.
Şimdi aynı şeyi işaretçilerle yapın
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];
}
}
Davranış aynıdır; ancak şimdi maybeModify'ın dizi [1] 'i hiç değiştirmediğini kanıtlamak çok daha zordur, çünkü zaten bir işaretçi verdik; Kedi çantadan çıktı. Şimdi çok daha zor bir kanıt yapmak zorunda: maybeModify'ın statik analizi ve asla x x 1'e yazdığını kanıtlamak için. zor.
Modern derleyiciler statik analizde gittikçe daha iyi hale geliyor, ancak onlara yardımcı olmak ve referansları kullanmak her zaman iyidir.
Tabii ki, bu tür akıllı optimizasyonları yasaklayan derleyiciler, gerektiğinde referansları gerçekten işaretleyicilere dönüştürecektir.
DÜZENLEME: Bu yanıtı gönderdikten beş yıl sonra, referansların aynı adresleme kavramına bakmanın farklı bir yolundan farklı olduğu gerçek bir teknik fark buldum. Referanslar, geçici nesnelerin ömrünü işaretçilerin yapamayacağı şekilde değiştirebilir.
F createF(int argument);
void extending()
{
const F& ref = createF(5);
std::cout << ref.getArgument() << std::endl;
};
Normalde çağrının oluşturduğu gibi geçici nesneler createF(5)
ifadenin sonunda yok edilir. Ancak, bu nesneyi bir başvuruya bağlayarak ref
, C ++ ref
kapsam dışı olana kadar bu geçici nesnenin ömrünü uzatır .
maybeModify
bununla ilgili herhangi bir şeyin adresini almadığını belirlemek, x
bir grup işaretçi aritmetiğinin gerçekleşmediğini kanıtlamaktan çok daha kolaydır.
void maybeModify(int& x) { 1[&x]++; }
, yukarıdaki diğer yorumların tartıştığı satırlarda yanlış yorum olarak eski olarak işaretlendiğini hissediyorum
Aslında, bir referans gerçekten bir işaretçi gibi değildir.
Bir derleyici, değişkenleri "başvurular" olarak saklar; bir ad bir bellek adresiyle ilişkilendirilir; bu, derleme sırasında herhangi bir değişken adını bir bellek adresine çevirmektir.
Bir başvuru oluşturduğunuzda, derleyiciye yalnızca işaretçi değişkenine başka bir ad atadığınızı bildirirsiniz; bu nedenle başvurular "null değerini gösteremez", çünkü bir değişken olamaz ve olamaz.
Göstergeler değişkenlerdir; başka bir değişkenin adresini içerir veya boş olabilir. Önemli olan bir işaretçi bir değere sahipken, bir referans sadece referans aldığı bir değişkene sahiptir.
Şimdi gerçek kodun bir açıklaması:
int a = 0;
int& b = a;
Burada işaret eden başka bir değişken oluşturmuyorsunuz a
; sadece değerini tutan bellek içeriğine başka bir ad ekliyorsunuz a
. Bu belleğin şimdi iki adı vardır a
ve her iki ad b
kullanılarak da adreslenebilir.
void increment(int& n)
{
n = n + 1;
}
int a;
increment(a);
Bir işlevi çağırırken, derleyici genellikle kopyalanacak argümanlar için bellek alanları oluşturur. İşlev imzası, yaratılması gereken boşlukları tanımlar ve bu boşluklar için kullanılması gereken adı verir. Bir parametrenin başvuru olarak bildirilmesi, derleyiciye yöntem çağrısı sırasında yeni bir bellek alanı ayırmak yerine giriş değişkeni bellek alanını kullanmasını söyler. İşlevinizin doğrudan çağrı kapsamında bildirilen bir değişkeni manipüle edeceğini söylemek garip gelebilir, ancak derlenmiş kodu çalıştırırken daha fazla kapsam olmadığını unutmayın; sadece düz bir bellek var ve fonksiyon kodunuz herhangi bir değişkeni değiştirebilir.
Şimdi, derleyicinizin, bir extern değişkeni kullanırken olduğu gibi, derleme sırasında referansı bilemeyebileceği bazı durumlar olabilir. Dolayısıyla, referans, temel kodda bir işaretçi olarak uygulanabilir veya uygulanmayabilir. Ancak size verdiğim örneklerde, büyük olasılıkla bir işaretçi ile uygulanmayacaktır.
Bir referans asla olamaz NULL
.
void Foo::bar() { virtual_baz(); }
segfaults gibi bir kod var . Referansların null olabileceğinin farkında değilseniz, null değerini başlangıç noktasına kadar izleyemezsiniz.
int &r=*p;
tanımsız bir davranıştır. Bu noktada, bir yok "NULL referans işaret," Eğer bir programa sahip artık hakkında gerekçeli edilebilir hiç .
Hem referanslar hem de işaretçiler dolaylı olarak başka bir değere erişmek için kullanılırken, referanslar ve işaretçiler arasında iki önemli fark vardır. Birincisi, referansın her zaman bir nesneye atıfta bulunmasıdır: Bir referansı başlatmadan tanımlamak bir hatadır. Atamanın davranışı ikinci önemli farktır: Bir referansa atama, referansın bağlı olduğu nesneyi değiştirir; başka bir nesneye yapılan başvuruyu yeniden hatırlatmaz. Başlatıldıktan sonra, başvuru her zaman aynı temel nesneyi ifade eder.
Bu iki program parçasını düşünün. İlkinde, bir işaretçiyi diğerine atarız:
int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2; // pi now points to ival2
Atamadan sonra, ival, pi ile adreslenen nesne değişmeden kalır. Atama, pi değerini değiştirerek farklı bir nesneye işaret eder. Şimdi iki referans atayan benzer bir programı düşünün:
int &ri = ival, &ri2 = ival2;
ri = ri2; // assigns ival2 to ival
Bu atama ival değerini, ri tarafından başvurulan değeri ve referansın kendisini değiştirmez. Atamadan sonra, iki referans hala orijinal nesnelerine atıfta bulunur ve bu nesnelerin değeri de şimdi aynıdır.
Bilgisayar dillerini soyut ya da akademik bir şekilde incelemeye aşina değilseniz ezoterik görünebilecek bir anlamsal fark vardır.
En üst düzeyde, referans fikri şeffaf "takma adlar" dır. Bilgisayarınız çalışmasını sağlamak için bir adres kullanabilir, ancak bunun için endişelenmeniz gerekmez: bunları mevcut bir nesne için "sadece başka bir ad" olarak düşünmeniz gerekir ve sözdizimi bunu yansıtır. İşaretçilerden daha katıdırlar, böylece derleyici sarkan bir referans oluşturmak üzereyken, sarkan bir işaretçi oluşturmaktan daha güvenilir bir şekilde sizi uyarabilir.
Bunun ötesinde, işaretçiler ve referanslar arasında elbette bazı pratik farklılıklar vardır. Bunları kullanmak için sözdizimi açıkça farklıdır ve referansları "yeniden oturtamaz", hiçbirliğe referans veremez veya referanslara işaret edemezsiniz.
Bir başvuru, başka bir değişkenin takma adıdır, oysa bir işaretçi bir değişkenin bellek adresini tutar. Başvurular genellikle işlev parametreleri olarak kullanılır, böylece iletilen nesne kopya değil nesnenin kendisidir.
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.
Ne kadar yer kapladığı önemli değildir, çünkü ne kadar yer kaplayacağına dair herhangi bir yan etki (kod çalıştırmadan) göremezsiniz.
Öte yandan, referanslar ve işaretçiler arasındaki en büyük fark, const referanslarına atanan geçicilerin const referansı kapsam dışına çıkana kadar yaşamasıdır.
Örneğin:
class scope_test
{
public:
~scope_test() { printf("scope_test done!\n"); }
};
...
{
const scope_test &test= scope_test();
printf("in scope\n");
}
yazdıracak:
in scope
scope_test done!
Bu, ScopeGuard'ın çalışmasına izin veren dil mekanizmasıdır.
Bu öğreticiye dayanmaktadır . Yazılanlar bunu daha açık hale getirir:
>>> 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)
Basitçe hatırlamak için,
>>> 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.)
Dahası, neredeyse herhangi bir işaretçi öğreticisine başvurabileceğimizden, işaretçi, işaretçiyi bir diziye benzer hale getiren işaretçi aritmetiği tarafından desteklenen bir nesnedir.
Aşağıdaki ifadeye bakın,
int Tom(0);
int & alias_Tom = Tom;
alias_Tom
olarak anlaşılabilir alias of a variable
(ile farklı typedef
olan alias of a type
) Tom
. Ayrıca böyle bir ifadenin terminolojisinin bir referans oluşturmak olduğunu unutmak da iyidir Tom
.
nullptr
musunuz? Bu konunun başka bir bölümünü gerçekten okudunuz mu, yoksa ...?
Referans, bir hafızaya verilen başka bir isim değildir. Kullanım sırasında otomatik olarak atıfta bulunulan değişmez bir işaretçi. Temel olarak aşağıdakilere kadar kaynar:
int& j = i;
Dahili olarak olur
int* const j = &i;
const
işaretçi uygulamak için geçerli bir yoldur . Bu esneklik, referans ve işaretçi arasında bir fark olduğunu kanıtlamaz.
C ++ 'da başvuru nedir? Nesne türü olmayan bazı özel tür örnekleri .
C ++ 'da işaretçi nedir? Nesne türü olan belirli türden bazı örnekler .
Gönderen nesne türünün ISO C ++ tanımı :
Bir nesne türü bir (muhtemelen bir ev bir işlev türü, bir referans tipi, ve değildir ulaşım kolaylığı) tip ev dışında tutulacaktır.
Bilmek önemli olabilir, nesne türü C ++ 'da tür evreninin üst düzey bir kategorisidir. Referans ayrıca üst düzey bir kategoridir. Ama işaretçi değil.
İşaretçiler ve referanslar bileşik türü bağlamında birlikte belirtilir . Bunun temel nedeni, referansı olmayan C'den miras alınan (ve uzatılmış) deklaratör sözdiziminin doğasıdır. (Ayrıca, C ++ 11'den bu yana birden fazla referans bildirgesi vardır, ancak işaretçiler hala "birleştirilmiş": &
+ &&
vs.. *
) Bu nedenle, bu bağlamda benzer C stiline sahip "uzantı" ile spesifik bir dil hazırlamak biraz makul . (Hala declarators atıkların sözdizimsel anlamlılık sözdizimi olduğunu iddia edecektir çok insan kullanıcılar ve uygulamalar hem sinir bozucu hale getirir. Böylece, hepsi edilecek kalifiye değilseniz yerleşikyeni bir dil tasarımında. Bu PL tasarımı hakkında tamamen farklı bir konudur.)
Aksi takdirde, işaretçilerin birlikte referanslarla birlikte belirli bir tür tür olarak nitelendirilebilmesi önemsizdir. Sözdizimi benzerliğinin yanı sıra çok az ortak özelliği paylaşırlar, bu nedenle çoğu durumda bunları bir araya getirmeye gerek yoktur.
Yukarıdaki ifadelere yalnızca "işaretçilerden" ve "referanslardan" tür olarak bahsedilmiştir. Örnekleri ile ilgili bazı sorular var (değişkenler gibi). Ayrıca çok fazla yanlış anlama var.
Üst düzey kategorilerin farklılıkları, zaten işaretçilerle doğrudan bağlantılı olmayan birçok somut farkı ortaya çıkarabilir:
cv
niteleyiciler bulunabilir. Referanslar yapılamaz.Referanslarla ilgili birkaç özel kural daha:
&&
Şablon parametre indirimi sırasında referans daralmasına dayalı olarak parametrelerle ilgili özel kurallar ("yönlendirme referansları" olarak) parametrelerin "kusursuzca iletilmesine" olanak tanır .std::initializer_list
referans ömrünün uzatılması için benzer bazı kurallar geçerlidir. Bir başka solucan konservesi.Referansların sözdizimsel şeker olduğunu biliyorum, bu yüzden kodun okunması ve yazılması daha kolay.
Teknik olarak, bu oldukça yanlıştır. Referanslar C ++ 'daki diğer özelliklerin sözdizimsel şekeri değildir, çünkü herhangi bir anlamsal farklılık olmadan tam olarak diğer özelliklerle değiştirilemezler.
(Benzer şekilde, lambda-sentezleme ler vardır değil tam da böyle "belirtilmemiş" özellikleri olan simüle olamaz çünkü C ++ da başka özellikler sözdizimsel şeker ele değişkenlerin tanımlanması için bu değişkenlerin başlatma sırası olabilir, çünkü önemli olabilir, önemli.)
C ++ bu katı anlamda sadece birkaç çeşit sözdizimsel şekere sahiptir. Bir örnek (C'den devralınmıştır) yerleşik (aşırı yüklenmemiş) operatördür []
; bu , yerleşik operatör tek *
ve ikili üzerinde belirli kombinasyon biçimlerinin tam olarak aynı semantik özelliklerine sahip olarak tanımlanır+
.
Dolayısıyla, bir işaretçi ve bir referans aynı miktarda bellek kullanır.
Yukarıdaki ifade yanlıştır. Bu tür yanlış anlamaları önlemek için, bunun yerine ISO C ++ kurallarına bakın:
Kaynaktan [intro.object] / 1 :
... Bir nesne, inşaat döneminde, ömrü boyunca ve yıkım döneminde bir depolama bölgesini işgal eder. ...
Kaynaktan [dcl.ref] / 4 :
Bir referansın depolama gerektirip gerektirmediği belirtilmemiştir.
Bunların semantik özellikler olduğuna dikkat edin .
Bu işaretçiler, dil tasarımı anlamında referanslarla bir araya getirilecek kadar nitelikli olmasalar bile, yine de, diğer bazı bağlamlarda, örneğin parametre türlerinde seçim yaparken aralarında seçim yapmayı tartışmalı hale getiren bazı argümanlar vardır.
Ama bu tüm hikaye değil. Demek istediğim, dikkat etmeniz gereken işaretçiler ve referanslardan daha fazla şey var.
Bu kadar özel seçimlere bağlı kalmanız gerekmiyorsa, çoğu durumda cevap kısadır: işaretçi kullanma zorunluluğunuz yoktur, bu yüzden kullanmazsınız . İşaretçiler genellikle yeterince kötüdür, çünkü beklemediğiniz kadar çok şey ima ederler ve kodun sürdürülebilirliğini ve (hatta) taşınabilirliğini zedeleyen çok fazla zımni varsayımlara güveneceklerdir. İşaretçilere gereksiz yere güvenmek kesinlikle kötü bir stildir ve modern C ++ anlamında kaçınılmalıdır. Amacınızı yeniden düşünün ve sonunda işaretçinin çoğu durumda son türlerin özelliği olduğunu göreceksiniz .
&
başvuru türü türlerini gerektirir . (Ve genellikle const
nitelikli olmalıdır .)&&
başvuru türü türleri gerektirir . (Ve genellikle niteleyici olmamalıdır.)operator=
Özel üye işlevleri olarak aşırı yüklenmiş , kopyalama / taşıma yapıcılarının 1. parametresine benzer referans türleri gerektirir.++
kukla gerektirir int
.unique_ptr
ve shared_ptr
(veya opak olmalarını istiyorsanız kendiniz homebrew olanlar gibi) akıllı işaretçiler kullanın .std::optional
, ham işaretçiler yerine sarıcı kullanın .observer_ptr
, Library Fundamental TS'deki gibi genellikle daha uygun bir şey vardır .Tek istisna mevcut dilde çözülemez:
operator new
. (Bununla birlikte, cv - void*
sıradan nesne işaretleyicilerine kıyasla hala oldukça farklı ve daha güvenlidir, çünkü GNU'lar gibi bazı uygun olmayan uzantılara güvenmediğiniz sürece beklenmedik işaretçi aritmetiklerini dışlar void*
.)Yani, pratikte cevap çok açık: şüphe duyduğunuzda işaretçilerden kaçının . İşaretçileri yalnızca, başka hiçbir şeyin daha uygun olmadığının çok açık nedenleri olduğunda kullanmalısınız. Yukarıda belirtilen birkaç istisnai durum dışında, bu tür seçimler neredeyse her zaman sadece C ++ 'a özgü değildir (ancak dile uygulamaya özel olması muhtemeldir). Bu tür örnekler şunlar olabilir:
Soruyu bazı Google arama sonuçlarıyla (C ++ 'a özgü olmayan) görmeye gelirseniz , bunun yanlış yer olması muhtemeldir.
C ++ 'da referanslar oldukça "tuhaftır", çünkü esasen birinci sınıf değildir: atıfta bulunulan nesneler veya işlevler olarak ele alınacaklardır, böylece sol işlenen olmak gibi birinci sınıf işlemleri destekleme şansları yoktur . üye erişim operatörü belirtilen nesnenin tipine bağımsız olarak. Diğer diller referanslarında benzer kısıtlamalara sahip olabilir veya olmayabilir.
C ++ 'da yapılan başvurular muhtemelen farklı dillerdeki anlamı korumaz. Örneğin, genel olarak referanslar C ++ 'da olduğu gibi değerler üzerinde null olmayan özellikler anlamına gelmez, bu nedenle bu tür varsayımlar diğer dillerde çalışmayabilir (ve karşı örnekleri çok kolay bulacaksınız, örneğin Java, C #, ...).
Genel olarak farklı programlama dillerinde referanslar arasında bazı ortak özellikler olabilir, ancak SO'daki diğer bazı sorular için bırakalım.
(Bir yan not: soru ALGOL 68 ve PL / I gibi herhangi bir "C benzeri" dilin dahil edilmesinden daha önce önemli olabilir .)
C ++ 'da bir işaretçiye başvuru mümkündür, ancak bunun tersi mümkün değildir, referansa bir işaretçinin mümkün olmadığı anlamına gelir. İşaretçiye yapılan başvuru, işaretçiyi değiştirmek için daha temiz bir sözdizimi sağlar. Şu örneğe bakın:
#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;
}
Ve yukarıdaki programın C sürümünü düşünün. C'de işaretçi işaretçi kullanmak zorunda (çoklu dolaylı), ve bu karışıklığa yol açar ve program karmaşık görünebilir.
#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;
}
İşaretçiye başvuru hakkında daha fazla bilgi için aşağıdakileri ziyaret edin:
Dediğim gibi, referansa bir işaretçi mümkün değildir. Aşağıdaki programı deneyin:
#include <iostream>
using namespace std;
int main()
{
int x = 10;
int *ptr = &x;
int &*ptr1 = ptr;
}
Bunlardan herhangi birine ihtiyaç duymadıkça referansları kullanıyorum:
Boş göstergeler, işlev aşırı yüklenmesini veya bir bool kullanımını önlemek için genellikle ucuz bir yol olan sentinel değeri olarak kullanılabilir.
Bir işaretçi üzerinde aritmetik yapabilirsiniz. Örneğin,p += offset;
&r + offset
nerede r
beyan edildiğini yazabilirsiniz
İşaretçiler ve referanslar arasında kimsenin bahsetmediğini gördüğüm temel bir fark vardır: referanslar fonksiyon argümanlarında referans by-pass anlambilimini etkinleştirir. İşaretçiler, başlangıçta görünmese de şunları yapmazlar: yalnızca by-pass semantik sağlarlar. Bu, bu makalede çok güzel açıklanmıştır .
Saygılarımızla, & rzej
Karışıklık ekleme riski altında, bazı girdileri atmak istiyorum, eminim çoğunlukla derleyicinin referansları nasıl uyguladığına bağlıdır, ancak gcc durumunda bir referansın yalnızca yığındaki bir değişkeni gösterebileceği fikri aslında doğru değil, örneğin bunu ele alalım:
#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;
}
Hangi çıktı:
THIS IS A STRING
0xbb2070 : 0xbb2070
Bellek adreslerinin bile tamamen aynı olduğunu fark ederseniz, referans, öbek üzerindeki bir değişkeni başarıyla işaret ediyor demektir! Şimdi gerçekten garip olmak istiyorsanız, bu da işe yarar:
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).*/
}
Hangi çıktı:
THIS IS A STRING
Bu nedenle bir referans başlık altında bir işaretçi IS, her ikisi de sadece adresin işaret ettiği bir bellek adresi saklıyor, std :: cout << str_ref denirse ne olacağını düşünüyorsunuz; Delete & str_ref çağrıldıktan SONRA? Açıkçası iyi derler, ancak çalışma zamanında bir segmentasyon hatasına neden olur, çünkü artık geçerli bir değişkene işaret etmiyor, aslında hala var olan (kapsamın dışına çıkana kadar), ancak işe yaramaz olan kırık bir referansımız var.
Başka bir deyişle, bir referans, işaretçi mekaniğinin soyutlanmış bir işaretçiden başka bir şey değildir, böylece daha güvenli ve kullanımı daha kolay hale getirir (yanlışlıkla işaretçi matematiği, karıştırma yok. 'Ve' -> 'vb.) yukarıdaki örneklerim gibi saçmalık denemeyin;)
Şimdi , bir derleyicinin referansları nasıl ele aldığından bağımsız olarak, her zaman kaputun altında bir tür işaretçi olacaktır , çünkü bir referansın beklendiği gibi çalışması için belirli bir bellek adresindeki belirli bir değişkene başvurması gerekir , bu nedenle bununla uğraşmak mümkün değildir (bu nedenle 'referans' terimi).
Referanslarla hatırlanması gereken tek önemli kural, bildirim sırasında tanımlanması gerektiğidir (bir başlıktaki bir referans haricinde, bu durumda, içerdiği nesne, onu tanımlamak için çok geç).
Unutmayın, yukarıdaki örneklerim sadece bir referansın ne olduğunu gösteren örnekler, asla bu referansı kullanmak istemeyeceksiniz! Bir referansın doğru kullanımı için, burada zaten kafasına çiviyi çarpan birçok cevap var.
Başka bir fark, bir void tipine işaretçileriniz olabilmesidir (ve herhangi bir şeye işaretçi anlamına gelir), ancak void referanslarının yasaklanmasıdır.
int a;
void * p = &a; // ok
void & p = a; // forbidden
Bu özel farktan gerçekten memnun olduğumu söyleyemem. Ben çok bir adres ve başka bir şekilde referanslar için aynı davranış ile bir şey anlam referans izin olacağını tercih ediyorum. Referanslar kullanarak memcpy gibi C kütüphanesi fonksiyonlarının bazı eşdeğerlerinin tanımlanmasına izin verir.
Ayrıca, satır içine alınmış bir işlevin parametresi olan bir başvuru, bir işaretçiden farklı bir şekilde işlenebilir.
void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
int testptr=0;
increment(&testptr);
}
void increftest()
{
int testref=0;
increment(testref);
}
Birçok derleyici, işaretçi sürümünü satır içine alırken, aslında belleğe bir yazma zorlar (adresi açıkça alıyoruz). Ancak, referansı daha uygun bir kayıtta bırakacaklardır.
Tabii ki, satır içi olmayan işlevler için işaretçi ve başvuru aynı kodu oluşturur ve işlev tarafından değiştirilmedikleri ve döndürülmedikleri takdirde, içsel değerleri değere göre geçirmek her zaman daha iyidir.
Referansların bir başka ilginç kullanımı, kullanıcı tanımlı türde varsayılan bir argüman sağlamaktır:
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;
}
Varsayılan lezzet, referansların 'geçici olarak bind başvurusu' yönünü kullanır.
Bu program sorunun cevabını anlamada yardımcı olabilir. Bu "j" referansının basit bir programı ve "x" değişkenini gösteren "ptr" işaretçisi.
#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();
}
Programı çalıştırın ve çıktı bir göz atın ve anlayacaksınız.
Ayrıca, 10 dakika ayırın ve bu videoyu izleyin: https://www.youtube.com/watch?v=rlJrrGV0iOg
Burada ele alınmamış başka bir nokta daha var gibi hissediyorum.
İşaretçilerin aksine, başvurular söz ettikleri nesneye sözdizimsel olarak eşdeğerdir , yani bir nesneye uygulanabilecek herhangi bir işlem bir başvuru için çalışır ve tam olarak aynı sözdizimiyle çalışır (istisna elbette başlatmadır).
Bu yüzeysel görünebilir olsa da, bu özellik C ++ özellikleri bir dizi için çok önemli olduğuna inanıyorum, örneğin:
Şablonlar . Şablon parametreleri ördek türünde olduğundan, türün sözdizimsel özellikleri önemlidir, bu nedenle genellikle aynı şablon hem T
ve ile birlikte kullanılabilir T&
.
(veya std::reference_wrapper<T>
hangi hala bir örtük döküm dayanır T&
)
o kapak Templates hem T&
ve T&&
hatta daha yaygındır.
Değerler . str[0] = 'X';
Referans olmadan ifadeyi düşünün, sadece c-dizeleri ( char* str
) için işe yarayacaktır . Karakteri başvuru ile döndürmek, kullanıcı tanımlı sınıfların aynı gösterime sahip olmasını sağlar.
Kopyalayıcılar . Sözdizimsel olarak, nesneleri kopyalayıcılara değil, nesnelere işaretleyicilere geçirmek mantıklıdır. Ancak, bir kopya oluşturucunun bir nesneyi değere göre almasının bir yolu yoktur - aynı kopya kurucusuna yinelemeli çağrı yapılmasına neden olur. Bu, referansları burada tek seçenek olarak bırakır.
Operatör aşırı yükleri . Referanslarla operator+(const T& a, const T& b)
, aynı infix gösterimini korurken bir operatör çağrısına dolaylı yoldan giriş yapmak mümkündür . Bu aynı zamanda aşırı yüklü işlevler için de geçerlidir.
Bu noktalar C ++ 'ın ve standart kütüphanenin önemli bir bölümünü güçlendirir, bu nedenle bu referansların oldukça büyük bir özelliğidir.
İşaretçiler ve referanslar arasında çok önemli bir teknik olmayan fark vardır: Bir işleve işaretçi ile iletilen bir argüman, sabit olmayan bir başvuru ile bir işleve iletilen bir argümandan çok daha görünürdür. Örneğin:
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)
}
C olarak, benzeyen bir çağrı fn(x)
sadece değere göre iletilebilir, bu yüzden kesinlikle değiştirilemez x
; bir argümanı değiştirmek için bir işaretçiyi iletmeniz gerekir fn(&x)
. Dolayısıyla, bir argümandan önce bir argüman gelmediyse, &
değiştirilmeyeceğini biliyordunuz. ( &
Değiştirilen anlamına gelen tersi doğru değildi, çünkü bazen büyük salt okunur yapıları const
işaretçiyle iletmeniz gerekir.)
Bazıları bunun kodu okurken böyle kullanışlı bir özellik const
olduğunu, işlev hiç beklemese bile , işaretçi parametrelerinin referans olmayanlardan ziyade değiştirilebilir parametreler için her zaman kullanılması gerektiğini savunur nullptr
. Yani, bu insanlar fn3()
yukarıdaki gibi işlev imzalarına izin verilmemesi gerektiğini savunuyorlar . Google'ın C ++ stil yönergeleri buna bir örnektir.
Bir işaretçi 0 olarak başlatılabilir ve bir referans değil. Aslında, başvuru bir nesneye de başvurmalıdır, ancak bir işaretçi boş gösterici olabilir:
int* p = 0;
Ama sahip olamayız int& p = 0;
ve aynı zamanda int& p=5 ;
.
Aslında bunu doğru bir şekilde yapmak için, önce bir nesneyi bildirmiş ve tanımlamış olmalıyız, o zaman o nesneye referans yapabiliriz, böylece önceki kodun doğru uygulanması şöyle olur:
Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;
Bir başka önemli nokta, işaretleyicinin bildirimini başlatma olmadan yapabilmemizdir, ancak her zaman değişken veya nesneye referans olması gereken referans durumunda böyle bir şey yapılamaz. Bununla birlikte, bir işaretçinin bu şekilde kullanılması risklidir, bu nedenle genellikle işaretçinin gerçekten bir şeye işaret edip etmediğini kontrol ederiz. Referans olması durumunda böyle bir kontrol gerekli değildir, çünkü bildirim sırasında bir nesneye göndermenin zorunlu olduğunu zaten biliyoruz.
Başka bir fark, işaretçinin başka bir nesneye işaret edebilmesidir, ancak referans her zaman aynı nesneye başvuruyor, bu örneği alalım:
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
Başka bir nokta: STL şablonu gibi bir şablonumuz olduğunda, bu tür bir sınıf şablonu, operatörü [] kullanarak kolayca okunmasını veya yeni değer atamasını sağlamak için her zaman bir işaretçi değil bir referans döndürür:
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
.
Fark, sabit olmayan işaret değişkeninin (sabit bir işaretçiyle karıştırılmaması gereken) programın yürütülmesi sırasında bir zamanda değiştirilebilmesidir, başlatma sırasında işaretçi semantiklerinin (&, *) kullanılmasını gerektirirken referanslar ayarlanabilir yalnızca (bu yüzden bunları yalnızca yapıcı başlatıcı listesinde ayarlayabilirsiniz, ancak bir şekilde değil) ve normal değer erişim semantiğini kullanabilirsiniz. Temelde referanslar, çok eski bir kitapta okuduğum gibi aşırı yükleme operatörlerine destek sağlamak için tanıtıldı. Bu iş parçacığında belirtildiği gibi - işaretçi 0 veya istediğiniz herhangi bir değere ayarlanabilir. 0 (NULL, nullptr), işaretçinin hiçbir şeyle başlatılmadığı anlamına gelir. Boş gösterici boş göstergesine bir hata. Ancak aslında işaretçi bazı doğru bellek konumlarına işaret etmeyen bir değer içerebilir. Başvurular sırayla, her zaman doğru türde bir rvalue sağladığınız için kullanıcının referans verilemeyen bir şeye referans başlatmasına izin vermemeye çalışır. Referans değişkeninin yanlış bir bellek konumuna başlatılmasının birçok yolu olmasına rağmen - bunu ayrıntılara derinlemesine incelemeniz daha iyidir. Makine düzeyinde hem işaretçi hem de referans, işaretçilerle eşit olarak çalışır. Diyelim ki temel referanslarda sözdizimsel şeker. rvalue referansları bundan farklıdır - doğal olarak yığın / yığın nesneleridir. Referans değişkeninin yanlış bir bellek konumuna başlatılmasının birçok yolu olmasına rağmen - bunu ayrıntılara derinlemesine incelemeniz daha iyidir. Makine düzeyinde hem işaretçi hem de referans, işaretçilerle eşit olarak çalışır. Diyelim ki temel referanslarda sözdizimsel şeker. rvalue referansları bundan farklıdır - doğal olarak yığın / yığın nesneleridir. Referans değişkeninin yanlış bir bellek konumuna başlatılmasının birçok yolu olmasına rağmen - bunu ayrıntılara derinlemesine incelemeniz daha iyidir. Makine düzeyinde hem işaretçi hem de referans, işaretçilerle eşit olarak çalışır. Diyelim ki temel referanslarda sözdizimsel şeker. rvalue referansları bundan farklıdır - doğal olarak yığın / yığın nesneleridir.
basit bir ifadeyle, bir referansın bir değişken için alternatif bir isim olduğunu söyleyebiliriz, ancak bir işaretçi başka bir değişkenin adresini tutan bir değişkendir. Örneğin
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 */