Arama sonucu bulunamazsa "NULL" nesnesi döndürür


95

C ++ 'da oldukça yeniyim, bu yüzden öğrenirken birçok Java izmiyle tasarım yapma eğilimindeyim. Neyse, Java, bir 'arama' yöntemi ile sınıf olsaydı bir nesne iade edeceğini Tbir gelen Collection< T >özel bir parametre eşleşti, o nesneyi döneceğini ve nesne koleksiyonunda bulunamadı, ben iade edeceğini null. Sonra arama fonksiyonumda sadece kontrol ederdimif(tResult != null) { ... }

C ++ 'da, nullnesne yoksa bir değer döndüremeyeceğimi öğreniyorum. Sadece çağıran işleve hiçbir nesnenin bulunmadığını bildiren T tipi bir 'gösterge' döndürmek istiyorum. Bir istisna atmak istemiyorum çünkü gerçekten istisnai bir durum değil.

Kodum şu anda böyle görünüyor:

class Node {
    Attr& getAttribute(const string& attribute_name) const {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return NULL; // what should this be?
    }

private:
    vector<Attr> attributes;
}

Bu tür bir işaretleyici verebilmek için nasıl değiştirebilirim?


6
İstisna ve NULL her zaman tek çözüm değildir. Genellikle bulunamadığını belirten bir değer seçebilirsiniz: örneğin, hiçbir öğe eşleşmezse std::find(first, last, value)döner last.
Cascabel

Yanıtlar:


72

C ++ 'da başvurular boş olamaz. Hiçbir şey bulunmazsa isteğe bağlı olarak null döndürmek istiyorsanız, bir referans değil, bir işaretçi döndürmeniz gerekir:

Attr *getAttribute(const string& attribute_name) const {
   //search collection
   //if found at i
        return &attributes[i];
   //if not found
        return nullptr;
}

Aksi takdirde, referansla geri dönmekte ısrar ediyorsanız, öznitelik bulunamazsa bir istisna atmalısınız.

(Bu arada, biraz da yöntem olma konusunda endişeli değilim constve bir sivil dönen constözelliği. Felsefi nedenlerden dolayı, dönen öneririm const Attr *. Ayrıca bu özniteliği değiştirmek isteyebilirsiniz, bir sivil ile aşırı yüklenmeye neden olabilir constyöntemle bir constöznitelik olmayan da döndürmek .)


2
Teşekkürler. Bu arada, böyle bir rutin tasarlamanın kabul edilen bir yolu bu mu?
aduric

6
@aduric: Evet. Referanslar, sonucun var olması gerektiğini ima eder. İşaretçiler sonucun var olmayabileceğini ima eder.
Bill

7
Merak ediyorum, c ++ 11 nullptryerine NULLşimdi dönelim mi?
Spektral

1
yes her zaman C ++ 11 ve sonrasında NULL yerine nullptr kullanın. eski sürümlerle geriye dönük olarak uyumlu olmanız gerekiyorsa, yapmayın
Conrad Jones

56

Burada birkaç olası cevap var. Var olabilecek bir şeyi iade etmek istersiniz. En az tercih ettiğimden en çok tercih edilene kadar değişen bazı seçenekler şunlardır:

  • Referans olarak dönün ve istisna ile sinyal bulunamıyor.

    Attr& getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            throw no_such_attribute_error;
    }

Öznitelikleri bulamamanın uygulamanın normal bir parçası olması muhtemeldir ve bu nedenle çok da istisnai değildir. Bunun ele alınması gürültülü olurdu. Boş referanslara sahip olmak tanımsız bir davranış olduğundan boş değer döndürülemez.

  • İşaretçiyle geri dön

    Attr* getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return &attributes[i];
       //if not found
            return nullptr;
    }

GetAttribute'dan gelen bir sonucun NULL olmayan bir işaretçi olup olmadığını kontrol etmeyi unutmak kolaydır ve kolay bir hata kaynağıdır.

  • Boost.Optional kullanın

    boost::optional<Attr&> getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return boost::optional<Attr&>();
    }

Boost :: Optional, burada tam olarak ne olup bittiğini belirtir ve böyle bir özniteliğin bulunup bulunmadığını incelemek için kolay yöntemlere sahiptir.


Yan not: std :: optional yakın zamanda C ++ 17'ye oylandı, bu nedenle bu yakın gelecekte "standart" bir şey olacak.


+1 Önce boost :: optional'den bahsedeceğim ve diğer alternatiflerden sadece kısaca bahsedeceğim.
Nemanja Trifunovic

Ya boost :: Optional'den bahsedildiğini gördüm ama çok fazla ek yük gerektirdiğini düşünüyordum. Kullanmak bu tür sorunlara en iyi yaklaşımsa, kullanmaya başlayacağım.
aduric

boost::optionalfazla ek yük içermez (dinamik ayırma yok), bu yüzden bu kadar harika. Bunu polimorfik değerlerle kullanmak, sarma referansları veya işaretçiler gerektirir.
Matthieu M.

2
@MatthieuM. Muhtemelen aduric'in bahsettiği genel gider performans değil, projeye harici bir kitaplık dahil etmenin maliyetiydi.
Swoogan

Cevabıma bir ek: std bileşeni olarak isteğe bağlı olarak, muhtemelen C ++ 17 olabilecek bir şeyi standartlaştırmak için bir hareket olduğunu unutmayın. Yani bu teknik hakkında bilgi sahibi olmaya değer.
Kaz Dragon

22

NULL dönüşünü temsil eden statik bir nesneyi kolayca oluşturabilirsiniz.

class Attr;
extern Attr AttrNull;

class Node { 
.... 

Attr& getAttribute(const string& attribute_name) const { 
   //search collection 
   //if found at i 
        return attributes[i]; 
   //if not found 
        return AttrNull; 
} 

bool IsNull(const Attr& test) const {
    return &test == &AttrNull;
}

 private: 
   vector<Attr> attributes; 
};

Ve bir kaynak dosyada bir yerde:

static Attr AttrNull;

NodeNull, Attr türünde olmamalı mı?
aduric


2

Java'da (veya C #) yaptığınız gibi yapamayacağınızı anladığınız gibi. İşte başka bir öneri, nesnenin referansını bir argüman olarak iletebilir ve bool değerini döndürebilirsiniz. Sonuç koleksiyonunuzda bulunursa, bunu iletilen referansa atayabilir ve 'true' döndürebilir, aksi takdirde 'false' döndürebilirsiniz. Lütfen bu kodu dikkate alın.

typedef std::map<string, Operator> OPERATORS_MAP;

bool OperatorList::tryGetOperator(string token, Operator& op)
{
    bool val = false;

    OPERATORS_MAP::iterator it = m_operators.find(token);
    if (it != m_operators.end())
    {
        op = it->second;
        val = true;
    }
    return val;
}

Yukarıdaki işlev, true döndürdüğünü bulursa, Operatörü anahtar 'belirteci' ile bulmalı ve değeri Operator & op parametresine atamalıdır.

Bu rutin için arayan kodu şuna benzer

Operator opr;
if (OperatorList::tryGetOperator(strOperator, opr))
{
    //Do something here if true is returned.
}

1

Burada NULL döndürememenizin nedeni, dönüş türünüzü olarak bildirmiş olmanızdır Attr&. Sonda &, dönüş değerini bir "başvuru" yapar, bu da temelde varolan bir nesneye yönelik boş olmaması garantili bir işaretçi. Eğer değişiklik return null edebilmek istiyorsanız Attr&için Attr*.


0

Geri dönemezsiniz NULLçünkü işlevin dönüş türü bir nesnedir referenceve a değil pointer.


-3

Bunu deneyebilirsiniz:

return &Type();

6
Bu kod parçacığı soruyu çözebilirken, bir açıklama eklemek, yayınınızın kalitesini artırmaya gerçekten yardımcı olur. Gelecekte okuyucular için soruyu yanıtlayacağınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceklerini unutmayın.
NathanOliver

Bu muhtemelen yöntem yığınındaki bir nesneye ölü bir referans döndürür, değil mi?
mpromonet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.