Sepotong kode ini secara konseptual melakukan hal yang sama untuk tiga pointer (inisialisasi pointer aman):
int* p1 = nullptr;
int* p2 = NULL;
int* p3 = 0;
Jadi, apa keuntungan dari menetapkan pointer nullptr
lebih dari memberi mereka nilai NULL
atau 0
?
int
danvoid *
tidak akan memilihint
versi daripadavoid *
versi saat menggunakannullptr
.f(nullptr)
berbeda darif(NULL)
. Tetapi sejauh menyangkut kode di atas (menugaskan ke variabel lokal), ketiga pointer persis sama. Satu-satunya keuntungan adalah keterbacaan kode.nullptr
butthat perbedaan antara 0 danNULL
Jawaban:
Dalam kode itu, sepertinya tidak ada keuntungan. Tetapi pertimbangkan fungsi kelebihan beban berikut:
Fungsi mana yang akan dipanggil? Tentu saja, maksudnya di sini adalah untuk menelepon
f(char const *)
, tetapi pada kenyataannyaf(int)
akan dipanggil! Itu adalah masalah besar 1 , bukan?Jadi, solusi untuk masalah tersebut adalah dengan menggunakan
nullptr
:Tentu saja, itu bukan satu-satunya keuntungan
nullptr
. Ini yang lain:Karena dalam templat, jenis
nullptr
disimpulkan sebagainullptr_t
, sehingga Anda dapat menulis ini:1. Dalam C ++,
NULL
didefinisikan sebagai#define NULL 0
, jadi pada dasarnyaint
, itulah mengapaf(int)
disebut.sumber
nullptr
? (Tidak. Saya tidak menuntut)NULL
diperlukan oleh standar untuk memiliki tipe integral, dan itulah sebabnya itu biasanya didefinisikan sebagai0
atau0L
. Juga saya tidak yakin saya suka itunullptr_t
berlebihan, karena hanya menangkap panggilan dengannullptr
, bukan dengan pointer nol dari jenis yang berbeda, seperti(void*)0
. Tapi saya percaya itu memiliki beberapa kegunaan, bahkan jika itu hanya menyelamatkan Anda mendefinisikan jenis tempat-nilai tunggal Anda sendiri berarti "tidak ada".nullptr
memiliki nilai numerik yang jelas sedangkan konstanta penunjuk nol tidak. Konstanta penunjuk nol dikonversi menjadi penunjuk nol jenis itu (apa pun itu). Diperlukan bahwa dua pointer nol dari jenis yang sama membandingkan secara identik, dan konversi boolean mengubah pointer null menjadifalse
. Tidak ada lagi yang dibutuhkan. Oleh karena itu, adalah mungkin bagi kompiler (konyol, tetapi mungkin) untuk menggunakan mis0xabcdef1234
atau nomor lain untuk penunjuk nol. Di sisi lain,nullptr
diperlukan untuk mengkonversi ke angka nol.f(nullptr)
tidak akan memanggil fungsi yang dimaksud? Ada lebih dari satu motivasi. Banyak hal berguna lainnya dapat ditemukan oleh programmer sendiri di tahun-tahun mendatang. Jadi Anda tidak bisa mengatakan bahwa ada hanya satu penggunaan yang benar darinullptr
.C ++ 11 memperkenalkan
nullptr
, ini dikenal sebagaiNull
pointer konstan dan ini meningkatkan keamanan jenis dan menyelesaikan situasi ambigu tidak seperti implementasi yang ada tergantung konstan pointer nolNULL
. Untuk bisa memahami kelebihan darinullptr
. pertama-tama kita perlu memahami apaNULL
dan apa masalah yang terkait dengannya.Apa
NULL
sebenarnya?Pre C ++ 11
NULL
digunakan untuk mewakili pointer yang tidak memiliki nilai atau pointer yang tidak menunjuk ke sesuatu yang valid. Berlawanan dengan gagasan populerNULL
bukanlah kata kunci dalam C ++ . Ini adalah pengidentifikasi yang didefinisikan dalam header perpustakaan standar. Singkatnya, Anda tidak dapat menggunakanNULL
tanpa menyertakan beberapa header perpustakaan standar. Pertimbangkan program Sampel :Keluaran:
Standar C ++ mendefinisikan NULL sebagai implementasi makro didefinisikan didefinisikan dalam file header perpustakaan standar tertentu. Asal usul NULL adalah dari C dan C ++ mewarisinya dari C. Standar C mendefinisikan NULL sebagai
0
atau(void *)0
. Tetapi dalam C ++ ada perbedaan yang halus.C ++ tidak dapat menerima spesifikasi ini apa adanya. Tidak seperti C, C ++ adalah bahasa yang sangat diketik (C tidak memerlukan pemeran eksplisit dari
void*
jenis apa pun, sementara C ++ mewajibkan pemeran eksplisit). Ini membuat definisi NULL yang ditentukan oleh standar C tidak berguna dalam banyak ekspresi C ++. Sebagai contoh:Jika NULL didefinisikan sebagai
(void *)0
, tak satu pun dari ekspresi di atas akan berfungsi.void *
kestd::string
.void *
fungsi dari penunjuk ke fungsi anggota diperlukan.Jadi tidak seperti C, C ++ Standard diamanatkan untuk mendefinisikan NULL sebagai numerik literal
0
atau0L
.Jadi, apa perlunya konstanta null pointer lain ketika kita
NULL
sudah memilikinya ?Meskipun komite Standar C ++ menghasilkan definisi NULL yang akan bekerja untuk C ++, definisi ini memiliki masalah yang sama. NULL bekerja cukup baik untuk hampir semua skenario tetapi tidak semua. Ini memberikan hasil yang mengejutkan dan keliru untuk skenario langka tertentu. Sebagai contoh :
Keluaran:
Jelas, maksudnya adalah untuk memanggil versi yang mengambil
char*
sebagai argumen, tetapi sebagai output menunjukkan fungsi yang mengambilint
versi dipanggil. Ini karena NULL adalah literal numerik.Selain itu, karena ini adalah implementasi yang ditentukan apakah NULL adalah 0 atau 0L, ada banyak kebingungan dalam resolusi fungsi yang berlebihan.
Program Sampel:
Menganalisa cuplikan di atas:
doSomething(char *)
seperti yang diharapkan.doSomething(int)
tetapi mungkinchar*
versi diinginkan karena0
IS juga merupakan pointer nol.NULL
didefinisikan sebagai0
, panggilandoSomething(int)
saat mungkindoSomething(char *)
dimaksudkan, mungkin mengakibatkan kesalahan logika saat runtime. JikaNULL
didefinisikan sebagai0L
, panggilan tidak jelas dan menghasilkan kesalahan kompilasi.Jadi, tergantung pada implementasi, kode yang sama dapat memberikan berbagai hasil, yang jelas tidak diinginkan. Secara alami, komite standar C ++ ingin memperbaiki ini dan itu adalah motivasi utama untuk nullptr.
Jadi apa
nullptr
dan bagaimana cara menghindari masalahNULL
?C ++ 11 memperkenalkan kata kunci baru
nullptr
untuk dijadikan konstanta penunjuk nol. Tidak seperti NULL, perilakunya tidak ditentukan oleh implementasi. Ini bukan makro tetapi memiliki tipe sendiri. nullptr memiliki tipestd::nullptr_t
. C ++ 11 mendefinisikan properti untuk nullptr secara tepat untuk menghindari kerugian NULL. Untuk meringkas propertinya:Properti 1: ia memiliki tipe sendiri
std::nullptr_t
, danProperti 2: itu secara implisit dapat dikonversi dan sebanding dengan semua tipe pointer atau tipe pointer-ke-anggota, tetapi
Properti 3: itu tidak secara implisit dapat dikonversi atau sebanding dengan tipe integral, kecuali untuk
bool
.Perhatikan contoh berikut:
Dalam program di atas,
char *
Versi panggilan , Properti 2 & 3Dengan demikian pengenalan nullptr menghindari semua masalah NULL tua yang baik.
Bagaimana dan di mana Anda harus menggunakan
nullptr
?Aturan praktis untuk C ++ 11 adalah mulai menggunakan
nullptr
kapan saja Anda akan menggunakan NULL di masa lalu.Referensi Standar:
C ++ 11 Standar: C.3.2.4 Makro NULL
C ++ 11 Standar: 18.2 Jenis
C ++ 11 Standar: 4.10 Konversi pointer
Standar C99: 6.3.2.3 Pointer
sumber
nullptr
, meskipun saya tidak tahu apa bedanya kode saya. Terima kasih atas jawaban yang luar biasa dan terutama untuk usahanya. Memberi saya banyak informasi tentang topik ini.0xccccc....
, tetapi, variabel kurang-nilai merupakan kontradiksi yang melekat.bool flag = nullptr;
). Tidak, tidak OK, saya mendapatkan kesalahan berikut pada waktu kompilasi dengan g ++ 6:error: converting to ‘bool’ from ‘std::nullptr_t’ requires direct-initialization [-fpermissive]
Motivasi sesungguhnya di sini adalah penerusan yang sempurna .
Mempertimbangkan:
Sederhananya, 0 adalah nilai khusus , tetapi nilai tidak dapat disebarkan melalui tipe sistem saja. Fungsi penerusan sangat penting, dan 0 tidak dapat mengatasinya. Oleh karena itu, mutlak diperlukan untuk memperkenalkan
nullptr
, di mana tipenya adalah apa yang spesial, dan tipenya memang bisa diperbanyak. Bahkan, tim MSVC harus memperkenalkannullptr
lebih awal dari jadwal setelah mereka menerapkan referensi nilai dan kemudian menemukan perangkap ini untuk diri mereka sendiri.Ada beberapa kasus sudut lain di mana
nullptr
dapat membuat hidup lebih mudah - tetapi itu bukan kasus inti, karena para pemain dapat memecahkan masalah ini. MempertimbangkanPanggilan dua kelebihan beban yang terpisah. Selain itu, pertimbangkan
Ini ambigu. Tapi, dengan nullptr, Anda bisa menyediakan
sumber
forward((int*)0)
bekerja. Apakah saya melewatkan sesuatu?Dasar-dasar nullptr
std::nullptr_t
adalah tipe dari null pointer literal, nullptr. Ini adalah nilai / nilai jenisstd::nullptr_t
. Ada konversi implisit dari nilai nullptr ke null dari semua jenis pointer.0 literal adalah int, bukan pointer. Jika C ++ menemukan dirinya melihat 0 dalam konteks di mana hanya sebuah pointer dapat digunakan, itu akan dengan enggan menafsirkan 0 sebagai pointer nol, tetapi itu adalah posisi mundur. Kebijakan utama C ++ adalah 0 adalah int, bukan pointer.
Keuntungan 1 - Hapus ambiguitas saat overloading pada pointer dan tipe integral
Dalam C + + 98, implikasi utama dari ini adalah bahwa kelebihan pada pointer dan tipe integral dapat menyebabkan kejutan. Melewati 0 atau NULL ke kelebihan seperti itu tidak pernah disebut kelebihan pointer:
Hal yang menarik tentang panggilan itu adalah kontradiksi antara makna yang tampak dari kode sumber (“Saya menyebut kesenangan dengan NULL-the null pointer”) dan artinya yang sebenarnya (“Saya menyebut kesenangan dengan semacam integer — bukan nol pointer ").
Keuntungan nullptr adalah tidak memiliki tipe integral. Memanggil fungsi overload menyenangkan dengan nullptr memanggil void * overload (mis., Pointer kelebihan beban), karena nullptr tidak dapat dilihat sebagai sesuatu yang tidak terpisahkan:
Menggunakan nullptr bukan 0 atau NULL sehingga menghindari kejutan resolusi kelebihan beban.
Keuntungan lain dari
nullptr
overNULL(0)
ketika menggunakan otomatis untuk tipe pengembalianMisalnya, anggap Anda menjumpai ini dalam basis kode:
Jika Anda tidak tahu (atau tidak bisa dengan mudah menemukan) apa yang ditemukan olehRecord, mungkin tidak jelas apakah hasilnya adalah tipe pointer atau tipe integral. Lagi pula, 0 (hasil apa yang diuji terhadap) bisa jalan baik. Jika Anda melihat yang berikut, di sisi lain,
tidak ada ambiguitas: hasil harus berupa tipe pointer.
Keuntungan 3
Kompilasi program di atas dan dijalankan dengan sukses tetapi lockAndCallF1, lockAndCallF2 & lockAndCallF3 memiliki kode yang berlebihan. Sangat disayangkan untuk menulis kode seperti ini jika kita dapat menulis template untuk semua ini
lockAndCallF1, lockAndCallF2 & lockAndCallF3
. Sehingga bisa digeneralisasi dengan template. Saya telah menulis fungsi templatelockAndCall
alih-alih beberapa definisilockAndCallF1, lockAndCallF2 & lockAndCallF3
untuk kode redundan.Kode difaktorkan ulang sebagai berikut:
Analisis detail mengapa kompilasi gagal karena
lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
tidak untuklockAndCall(f3, f3m, nullptr)
Mengapa kompilasi
lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
gagal?Masalahnya adalah bahwa ketika 0 diteruskan ke lockAndCall, pengurangan tipe templat menendang untuk mengetahui jenisnya. Tipe 0 adalah int, jadi itulah tipe ptr parameter di dalam instantiasi panggilan ini ke lockAndCall. Sayangnya, ini berarti bahwa dalam panggilan ke func di dalam lockAndCall, sebuah int dikirimkan, dan itu tidak kompatibel dengan
std::shared_ptr<int>
parameter yangf1
diharapkan. 0 yang diteruskan dalam panggilan kelockAndCall
dimaksudkan untuk mewakili pointer nol, tetapi yang benar-benar diteruskan adalah int. Mencoba meneruskan int ini ke f1 karenastd::shared_ptr<int>
merupakan jenis kesalahan. PanggilanlockAndCall
dengan 0 gagal karena di dalam templat, int dikirimkan ke fungsi yang memerlukan astd::shared_ptr<int>
.Analisis untuk panggilan yang terlibat
NULL
pada dasarnya sama. KetikaNULL
diteruskan kelockAndCall
, tipe integral disimpulkan untuk parameter ptr, dan kesalahan tipe terjadi ketikaptr
— tipe int atau int-like — diteruskan kef2
, yang mengharapkan mendapatkan astd::unique_ptr<int>
.Sebaliknya, panggilan yang terlibat
nullptr
tidak memiliki masalah. Ketikanullptr
diteruskan kelockAndCall
, tipe untukptr
dideduksi menjadistd::nullptr_t
. Ketikaptr
diteruskan kef3
, ada konversi implisit daristd::nullptr_t
keint*
, karenastd::nullptr_t
secara implisit mengkonversi ke semua jenis pointer.Dianjurkan, Setiap kali Anda ingin merujuk ke pointer nol, gunakan nullptr, bukan 0 atau
NULL
.sumber
Tidak ada keuntungan langsung dari
nullptr
cara Anda menunjukkan contoh.Tetapi pertimbangkan situasi di mana Anda memiliki 2 fungsi dengan nama yang sama; 1 mengambil
int
dan satu lagiint*
Jika Anda ingin menelepon
foo(int*)
dengan mengirimkan NULL, maka caranya adalah:nullptr
membuatnya lebih mudah dan intuitif :Tautan tambahan dari halaman web Bjarne.
Tidak relevan tetapi pada catatan sisi C ++ 11:
sumber
decltype(nullptr)
adalahstd::nullptr_t
.typedef decltype(nullptr) nullptr_t;
. Saya kira saya bisa melihat dalam standar. Ah, menemukannya: Catatan: std :: nullptr_t adalah tipe berbeda yang bukan tipe pointer atau pointer ke tipe anggota; sebaliknya, nilai awal dari jenis ini adalah konstanta penunjuk nol dan dapat dikonversi ke nilai penunjuk nol atau nilai penunjuk anggota nol.nullptr
.Seperti yang sudah dikatakan orang lain, keunggulan utamanya terletak pada kelebihan beban. Dan sementara
int
overload eksplisit vs pointer bisa jarang terjadi, pertimbangkan fungsi-fungsi pustaka standar sepertistd::fill
(yang telah menggigit saya lebih dari sekali di C ++ 03):Tidak kompilasi:
Cannot convert int to MyClass*
.sumber
IMO lebih penting daripada masalah kelebihan: dalam konstruksi templat bersarang mendalam, sulit untuk tidak kehilangan jejak jenisnya, dan memberikan tanda tangan eksplisit adalah upaya yang cukup. Jadi untuk semua yang Anda gunakan, semakin tepat fokus ke tujuan yang dimaksud, semakin baik, itu akan mengurangi kebutuhan untuk tanda tangan eksplisit dan memungkinkan kompiler untuk menghasilkan pesan kesalahan yang lebih berwawasan ketika terjadi kesalahan.
sumber