Buradaki cevapların çoğu, bir işlev imzasında ham bir göstergeye sahip olma konusundaki doğal belirsizliği, ifade etme anlamında ele almamaktadır. Sorunlar şunlardır:
Arayan, işaretçinin tek bir nesneyi mi yoksa nesnelerin bir "dizisinin" başlangıcını mı işaret ettiğini bilmez.
Arayan, işaretçinin işaret ettiği belleğe "sahip olup olmadığını" bilmez. IE, fonksiyonun hafızayı boşaltması gerekip gerekmediği. ( foo(new int)
- Bu bir bellek sızıntısı mı?).
Arayan nullptr
, işleve güvenli bir şekilde aktarılıp aktarılamayacağını bilmiyor .
Bu sorunların tümü referanslarla çözülmüştür:
Referanslar her zaman tek bir nesneyi ifade eder.
Referanslar asla bahsettikleri belleğe sahip değildir, sadece belleğe bir bakıştır.
Referanslar boş olamaz.
Bu referansları genel kullanım için çok daha iyi bir aday yapar. Bununla birlikte, referanslar mükemmel değildir - dikkate alınması gereken birkaç önemli sorun vardır.
- Açık bir dolaylama yok. Ham işaretçiyle ilgili bir sorun değil, çünkü
&
gerçekten bir işaretçiyi geçtiğimizi göstermek operatörü gerekiyor. Örneğin,int a = 5; foo(a);
burada a'nın referans ile geçildiği ve değiştirilebileceği açık değildir.
- Nullability. İşaretçilerin bu zayıflığı, aslında referanslarımızın geçersiz kılınmasını istediğimizde de bir güç olabilir.
std::optional<T&>
Geçerli olmadığını gören (iyi nedenlerle), işaretçiler bize istediğiniz bu boşluğu verir.
Görünüşe göre, açık dolaylı olarak null olabilecek bir referans istediğimizde, T*
hak elde etmek ? Yanlış!
Soyutlamalar
Nullabilite için umutsuzluğumuzda, T*
daha önce listelenen tüm eksikliklere ve anlamsal belirsizliklere ulaşabilir ve bunları göz ardı edebiliriz. Bunun yerine, C ++ 'ın en iyi yaptığı şeye ulaşmalıyız: bir soyutlama. Sadece bir işaretçiyi saran bir sınıf yazarsak, ifade edilebilme özelliğinin yanı sıra nullabilite ve açık dolaylılık da kazanırız.
template <typename T>
struct optional_ref {
optional_ref() : ptr(nullptr) {}
optional_ref(T* t) : ptr(t) {}
optional_ref(std::nullptr_t) : ptr(nullptr) {}
T& get() const {
return *ptr;
}
explicit operator bool() const {
return bool(ptr);
}
private:
T* ptr;
};
Bu, karşılaşabileceğim en basit arayüz, ancak işi etkili bir şekilde yapıyor. Referansı başlatmaya, bir değerin var olup olmadığını kontrol etmeye ve değere erişmeye izin verir. Bunu şu şekilde kullanabiliriz:
void foo(optional_ref<int> x) {
if (x) {
auto y = x.get();
// use y here
}
}
int x = 5;
foo(&x); // explicit indirection here
foo(nullptr); // nullability
Hedeflerimize ulaştık! Şimdi ham işaretçiye kıyasla avantajları görelim.
- Arayüz, referansın sadece bir nesneye atıfta bulunması gerektiğini açıkça göstermektedir.
- Açıkçası, kullanıcı tanımlı bir yıkıcıya ve hafızayı silme yöntemine sahip olmadığı için, atıfta bulunduğu belleğe sahip değildir.
nullptr
İşlev yazarı açıkça bir istekte bulunduğundan, arayan kişinin iletilebileceğini biliroptional_ref
Arayüzü buradan daha karmaşık hale getirebiliriz, örneğin eşitlik operatörleri, bir monadik get_or
ve map
arayüz, değeri alan veya bir istisna atan bir yöntem,constexpr
destek. Bu sizin tarafınızdan yapılabilir.
Sonuç olarak, ham işaretçiler kullanmak yerine, bu işaretçilerin kodunuzda gerçekte ne anlama geldiğinin nedeni ve standart bir kütüphane soyutlamasından faydalanın veya kendinizinkini yazın. Bu, kodunuzu önemli ölçüde geliştirecektir.
new
Bir işaretçi ve bunun sonucunda ortaya çıkan sahiplik sorunlarını oluşturmayı unutmayın .