Aynı Şeyin Bir Başka Adı
Fonksiyon parametreleri sayfasında her argüman fonksiyonun içine kopyalanıyordu. Bir fonksiyonun çağıranın değişkenini değiştirememesinin nedeni bu kopyadır — fonksiyon yalnızca kendi kopyasını görür. Bir referans bu duvarı yıkar. O bir takma addır: var olan bir değişkene bağlanmış, tam olarak aynı belleği paylaşan ikinci bir isim.
Bir referansı bildirimde & ile oluşturursunuz. Bir kez bağlandıktan sonra, referans ve orijinal ayırt edilemez:
İki kural referansları güvenli ve öngörülebilir kılar: bir referans bildirildiği anda başlatılmalıdır (int& r; bir derleme hatasıdır) ve sonradan başka bir şeye işaret edecek şekilde asla yeniden bağlanamaz. Bir referansa atama yapmak her zaman onun ilk bağlandığı şeye yazar.
Referansla Geçirme: Bir Fonksiyonun Geriye Uzanmasını Sağlayın
Asıl kazanç fonksiyonlardadır. Bir parametreye & koyun ve fonksiyon, çağıranın argümanının bir kopyasını değil, ona bir takma ad alır. Artık fonksiyon içindeki değişiklikler dışarıda da görünür:
& işaretini kaldırın, addBonus atılacak bir kopyayı artırır ve total 100 olarak kalır. Bu tek karakter farkın tamamıdır. Bu, birden fazla sonuç döndüren veya girdisini yerinde düzenleyen bir fonksiyon yazmanın standart yoludur. Klasik örnek iki değişkeni takas etmektir:
Referanslar olmadan swapValues yalnızca yerel kopyaları takas eder ve x/y 1 2 olarak kalırdı. (Standart kütüphanede zaten std::swap var, ama kendiniz yazmak bir referans parametresinin tam olarak ne sağladığını gösterir.)
const Referanslar: Hızlı Oku, Dokunmamaya Söz Ver
Referansla geçirme kopyalamadan da kaçınır — ve büyük bir nesne için bu kopya pahalı olabilir. Ama sade bir T& parametresi "bunu değiştirebilirim" sinyali verir, ki yalnızca okumak istediğinizde bu yanıltıcıdır. Çözüm const T&: bir referansın kopyasız hızını ve fonksiyonun argümanı değiştirmeyeceğine dair derleyici tarafından zorlanan bir söz elde edersiniz.
const olmayan bir referans yalnızca değiştirilebilir bir değişkene bağlanabilir, ama const bir referans değişmezlere ve geçici nesnelere de bağlanabilir — bu yüzden greet("literal works too") derlenir. Parametre türü seçmek için pratik bir kural:
void f(int x) // ucuz tür, salt okunur -> sadece kopyala
void f(const string& s) // ağır tür, salt okunur -> const referans
void f(string& s) // çağıranın nesnesini değiştirmeyi düşünüyorsun
Yalnızca okuduğunuz herhangi bir sınıf türü için (string, vector, kendi struct'larınız) varsayılan olarak const T& kullanın ve const olmayan bir referansı gerçekten geri yazmak istediğiniz durumlar için saklayın.
Bir Referans Döndürmek
Bir fonksiyon ayrıca bir referans döndürebilir, çağırana zaten var olan bir şeye takma ad verir. Bu, konteyner benzeri kodlarda yaygındır — v[i] = 5 ifadesinin çalışmasını sağlayan ve operator[]'nin perde arkasında yaptığı şey budur:
at, int& döndürdüğü için, at(data, 1) çağrı ifadesi kendisi atama yapabileceğiniz bir lvalue'dur. Bunun yerine sade bir int döndürün ve at(data, 1) = 42 derlenmezdi — geçici bir kopyaya atama yapıyor olurdunuz.
Büyük Tuzak: Sarkan Referanslar
Bir referans hiçbir şeye sahip değildir; yalnızca başka bir yerde yaşayan belleğe işaret eder. Referans hâlâ kullanılırken o bellek ölürse, elinizde bir sarkan referans olur ve onun üzerinden okumak tanımsız davranıştır — çöp basabilir, çökebilir veya üretimde gününüzü mahvedene kadar çalışıyormuş gibi görünebilir. Klasik hata, yerel bir değişkene referans döndürmektir:
int& broken() {
int local = 42;
return local; // HATA: broken() dönünce local yok edilir
} // döndürülen referans sarkar
int main() {
int& r = broken();
cout << r << "\n"; // TANIMSIZ DAVRANIŞ - ölü belleği okur
}
local değişkeni broken döner dönmez yok olur, bu yüzden referans geri kazanılmış yığın alanına işaret eder. Yalnızca çağrıdan daha uzun yaşayan bir şeye referans döndürün — referansla geçirilen bir parametre, bir veri üyesi veya bir static. Değer fonksiyon içinde hesaplanıyorsa, bunun yerine değerle döndürün ve kopyayı derleyicinin optimize etmesine izin verin. Aynı tuzak aralık tabanlı döngüleri ve geçici bir nesneye bağlı her referansı vurur: bir referansı, isimlendirdiği şeyin yaşam süresinin ötesinde asla tutmayın.
Sırada: Fonksiyon Aşırı Yükleme
Referanslar size her parametrede ikinci bir ayar düğmesi verir — kopya mı takma ad mı, değiştirilebilir mi const mu — ve bu düğme doğrudan bir sonraki konuyla etkileşir. Sırada, fonksiyon aşırı yükleme aynı ada ama farklı parametre listelerine sahip birkaç fonksiyon tanımlamanıza olanak tanır ve derleyici, argüman türlerini eşleştirerek doğru olanı seçer — değerle mi, referansla mı, yoksa const referansla mı geçirildikleri dahil.
Sıkça Sorulan Sorular
C++'ta referans nedir?
Referans, var olan bir değişkenin takma adıdır — aynı belleğe verilen başka bir isim. Bildirimde & ile oluşturursunuz: int& r = x;. Bundan sonra r ve x birbirinin yerine geçer; birini değiştirmek diğerini değiştirir. Referanslar bildirildiğinde başlatılmalıdır ve asla başka bir değişkene işaret edecek şekilde yeniden bağlanamaz.
C++'ta değerle geçirme ile referansla geçirme arasındaki fark nedir?
Değerle geçirme (void f(int x)) argümanı kopyalar, bu yüzden fonksiyon kendi kopyası üzerinde çalışır ve çağıranın değişkenine dokunulmaz. Referansla geçirme (void f(int& x)) fonksiyona çağıranın değişkenine doğrudan erişim verir, böylece değişiklikler çağrıdan sonra görünür olur — ve hiç kopya oluşturulmaz, ki bu büyük nesneler için önemlidir.
C++'ta const referans parametrelerini ne zaman kullanmalıyım?
Bir fonksiyon bir parametreyi yalnızca okuması gerektiğinde ama tür kopyalamak için pahalı olduğunda (string, vector, büyük struct'lar) const T& kullanın. Bir referansın kopyasız hızını, artı fonksiyonun çağıranın değerini değiştirmeyeceğine dair derleyici güvencesini elde edersiniz. int veya double gibi ucuz türler için sade değerle geçirme daha basit ve aynı derecede hızlıdır.