Kembalikan objek "NULL" jika hasil pencarian tidak ditemukan

94

Saya cukup baru mengenal C ++ jadi saya cenderung mendesain dengan banyak Java-isme saat saya belajar. Bagaimanapun, di Jawa, jika saya memiliki kelas dengan metode 'pencarian' yang akan mengembalikan objek Tdari Collection< T >yang cocok dengan parameter tertentu, saya akan mengembalikan objek itu dan jika objek tidak ditemukan dalam koleksi, saya akan kembali null. Kemudian dalam fungsi panggilan saya, saya hanya akan memeriksaif(tResult != null) { ... }

Di C ++, saya menemukan bahwa saya tidak dapat mengembalikan nullnilai jika objek tidak ada. Saya hanya ingin mengembalikan 'indikator' tipe T yang memberi tahu fungsi pemanggil bahwa tidak ada objek yang ditemukan. Saya tidak ingin memberikan pengecualian karena ini bukanlah keadaan yang luar biasa.

Seperti inilah tampilan kode saya sekarang:

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;
}

Bagaimana saya bisa mengubahnya sehingga saya bisa memberikan penanda semacam itu?

aduric
sumber
6
Pengecualian dan NULL tidak selalu satu-satunya solusi. Anda sering kali dapat memilih nilai untuk dikembalikan yang menunjukkan tidak ditemukan: misalnya, std::find(first, last, value)kembali lastjika tidak ada elemen yang cocok.
Kaskabel

Jawaban:

70

Di C ++, referensi tidak boleh null. Jika Anda ingin mengembalikan null secara opsional jika tidak ada yang ditemukan, Anda perlu mengembalikan pointer, bukan referensi:

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

Sebaliknya, jika Anda bersikeras untuk kembali dengan referensi, Anda harus membuat pengecualian jika atribut tidak ditemukan.

(Ngomong-ngomong, saya sedikit khawatir tentang metode Anda menjadi constdan mengembalikan non- constatribut. Untuk alasan filosofis, saya sarankan kembali const Attr *. Jika Anda juga mungkin ingin memodifikasi atribut ini, Anda dapat membebani dengan non- constmetode mengembalikan non- constatribut juga.)

Jesse Beder
sumber
2
Terima kasih. Ngomong-ngomong, apakah ini cara yang diterima untuk mendesain rutinitas seperti itu?
aduric
6
@aduric: Ya. Referensi menyiratkan bahwa hasil harus ada. Petunjuk menyiratkan bahwa hasilnya mungkin tidak ada.
Tagihan
7
Hanya ingin tahu, akan kita kembali nullptrbukan NULLuntuk c ++ 11 sekarang?
Spectral
1
ya selalu gunakan nullptr melalui NULL di C ++ 11 dan yang lebih baru. jika Anda perlu kompatibel dengan versi sebelumnya maka jangan
Conrad Jones
56

Ada beberapa kemungkinan jawaban di sini. Anda ingin mengembalikan sesuatu yang mungkin ada. Berikut ini beberapa pilihan, mulai dari yang paling tidak saya sukai hingga paling disukai:

  • Kembali dengan referensi, dan sinyal tidak dapat ditemukan dengan pengecualian.

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

Sepertinya tidak menemukan atribut adalah bagian normal dari eksekusi, dan karenanya tidak terlalu luar biasa. Penanganan untuk ini akan berisik. Nilai null tidak dapat dikembalikan karena perilaku tidak ditentukan memiliki referensi null.

  • Kembali dengan penunjuk

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

Sangat mudah untuk lupa memeriksa apakah hasil dari getAttribute akan menjadi penunjuk non-NULL, dan merupakan sumber bug yang mudah.

  • Gunakan Boost.Optional

    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&>();
    }

A boost :: opsional menandakan dengan tepat apa yang terjadi di sini, dan memiliki metode mudah untuk memeriksa apakah atribut seperti itu ditemukan.


Catatan tambahan: std :: optional baru-baru ini terpilih menjadi C ++ 17, jadi ini akan menjadi hal "standar" dalam waktu dekat.

Kaz Dragon
sumber
+1 Saya hanya akan menyebutkan boost :: opsional terlebih dahulu, dan hanya menyebutkan alternatif lainnya secara singkat.
Nemanja Trifunovic
Ya saya melihat boost :: opsional disebutkan di suatu tempat tetapi saya berpikir itu membutuhkan terlalu banyak overhead. Jika menggunakannya adalah pendekatan terbaik untuk masalah semacam ini, saya akan mulai menggunakannya.
aduric
boost::optionaltidak melibatkan banyak overhead (tidak ada alokasi dinamis), itulah mengapa ini sangat bagus. Menggunakannya dengan nilai polimorfik membutuhkan pembungkusan referensi atau pointer.
Matthieu M.
2
@Bayu_joo Sepertinya overhead aduric yang dimaksud bukanlah kinerja, tetapi biaya termasuk perpustakaan eksternal ke dalam proyek.
Swoogan
Sebuah tambahan untuk jawaban saya: perhatikan bahwa ada gerakan sedang terjadi untuk membakukan opsional sebagai komponen std, mungkin untuk apa yang mungkin C ++ 17. Jadi perlu diketahui tentang teknik ini.
Kaz Dragon
22

Anda dapat dengan mudah membuat objek statis yang mewakili pengembalian NULL.

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; 
};

Dan di suatu tempat di file sumber:

static Attr AttrNull;
Mark Ransom
sumber
Bukankah seharusnya NodeNull bertipe Attr?
aduric
3

Jika Anda menginginkan NULLnilai kembali, Anda perlu menggunakan pointer, bukan referensi.

Referensi tidak bisa dengan sendirinya NULL.

(Catatan untuk poster komentar di masa mendatang: Ya, Anda dapat memiliki alamat referensi menjadi NULL jika Anda benar-benar mencoba).

Lihat jawaban saya di sini untuk daftar perbedaan antara referensi dan petunjuk .

Brian R. Bondy
sumber
2

Seperti yang telah Anda ketahui bahwa Anda tidak dapat melakukannya seperti yang Anda lakukan di Java (atau C #). Berikut adalah saran lain, Anda bisa meneruskan referensi objek sebagai argumen dan mengembalikan nilai bool. Jika hasilnya ditemukan dalam koleksi Anda, Anda dapat menetapkannya ke referensi yang diteruskan dan mengembalikan 'true', jika tidak mengembalikan 'false'. Harap pertimbangkan kode ini.

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;
}

Fungsi di atas harus menemukan Operator terhadap kunci 'token', jika menemukan yang mengembalikan nilai true dan menetapkan nilai ke parameter Operator & op.

Kode penelepon untuk rutinitas ini terlihat seperti ini

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

Alasan mengapa Anda tidak dapat mengembalikan NULL di sini adalah karena Anda telah menyatakan tipe pengembalian Anda sebagai Attr&. Trailing &membuat nilai yang dikembalikan menjadi "referensi", yang pada dasarnya adalah penunjuk yang dijamin bukan untuk menjadi null ke objek yang ada. Jika Anda ingin mengembalikan null, ubah Attr&ke Attr*.

JSB ձոգչ
sumber
0

Anda tidak dapat mengembalikan NULLkarena tipe kembalian dari fungsi tersebut adalah objek referencedan bukan pointer.

codaddict
sumber
-3

Anda bisa mencoba ini:

return &Type();
Dibuat
sumber
6
Meskipun cuplikan kode ini dapat menyelesaikan pertanyaan, menyertakan penjelasan sangat membantu meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa mendatang, dan orang-orang itu mungkin tidak tahu alasan saran kode Anda.
NathanOliver
Ini mungkin mengembalikan referensi mati ke objek di tumpukan metode, bukan?
mpromonet