Dasar-dasar nullptr
std::nullptr_t
adalah tipe dari null pointer literal, nullptr. Ini adalah nilai / nilai jenis std::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:
void fun(int); // two overloads of fun
void fun(void*);
fun(0); // calls f(int), not fun(void*)
fun(NULL); // might not compile, but typically calls fun(int). Never calls fun(void*)
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:
fun(nullptr); // calls fun(void*) overload
Menggunakan nullptr bukan 0 atau NULL sehingga menghindari kejutan resolusi kelebihan beban.
Keuntungan lain dari nullptr
over NULL(0)
ketika menggunakan otomatis untuk tipe pengembalian
Misalnya, anggap Anda menjumpai ini dalam basis kode:
auto result = findRecord( /* arguments */ );
if (result == 0) {
....
}
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,
auto result = findRecord( /* arguments */ );
if (result == nullptr) {
...
}
tidak ada ambiguitas: hasil harus berupa tipe pointer.
Keuntungan 3
#include<iostream>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
int f1(std::shared_ptr<int> spw) // call these only when
{
//do something
return 0;
}
double f2(std::unique_ptr<int> upw) // the appropriate
{
//do something
return 0.0;
}
bool f3(int* pw) // mutex is locked
{
return 0;
}
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxtexGuard = std::lock_guard<std::mutex>;
void lockAndCallF1()
{
MuxtexGuard g(f1m); // lock mutex for f1
auto result = f1(static_cast<int>(0)); // pass 0 as null ptr to f1
cout<< result<<endl;
}
void lockAndCallF2()
{
MuxtexGuard g(f2m); // lock mutex for f2
auto result = f2(static_cast<int>(NULL)); // pass NULL as null ptr to f2
cout<< result<<endl;
}
void lockAndCallF3()
{
MuxtexGuard g(f3m); // lock mutex for f2
auto result = f3(nullptr);// pass nullptr as null ptr to f3
cout<< result<<endl;
} // unlock mutex
int main()
{
lockAndCallF1();
lockAndCallF2();
lockAndCallF3();
return 0;
}
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 template lockAndCall
alih-alih beberapa definisi lockAndCallF1, lockAndCallF2 & lockAndCallF3
untuk kode redundan.
Kode difaktorkan ulang sebagai berikut:
#include<iostream>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
int f1(std::shared_ptr<int> spw) // call these only when
{
//do something
return 0;
}
double f2(std::unique_ptr<int> upw) // the appropriate
{
//do something
return 0.0;
}
bool f3(int* pw) // mutex is locked
{
return 0;
}
std::mutex f1m, f2m, f3m; // mutexes for f1, f2, and f3
using MuxtexGuard = std::lock_guard<std::mutex>;
template<typename FuncType, typename MuxType, typename PtrType>
auto lockAndCall(FuncType func, MuxType& mutex, PtrType ptr) -> decltype(func(ptr))
//decltype(auto) lockAndCall(FuncType func, MuxType& mutex, PtrType ptr)
{
MuxtexGuard g(mutex);
return func(ptr);
}
int main()
{
auto result1 = lockAndCall(f1, f1m, 0); //compilation failed
//do something
auto result2 = lockAndCall(f2, f2m, NULL); //compilation failed
//do something
auto result3 = lockAndCall(f3, f3m, nullptr);
//do something
return 0;
}
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 yang f1
diharapkan. 0 yang diteruskan dalam panggilan ke lockAndCall
dimaksudkan untuk mewakili pointer nol, tetapi yang benar-benar diteruskan adalah int. Mencoba meneruskan int ini ke f1 karena std::shared_ptr<int>
merupakan jenis kesalahan. Panggilan lockAndCall
dengan 0 gagal karena di dalam templat, int dikirimkan ke fungsi yang memerlukan a std::shared_ptr<int>
.
Analisis untuk panggilan yang terlibat NULL
pada dasarnya sama. Ketika NULL
diteruskan ke lockAndCall
, tipe integral disimpulkan untuk parameter ptr, dan kesalahan tipe terjadi ketika ptr
— tipe int atau int-like — diteruskan ke f2
, yang mengharapkan mendapatkan a std::unique_ptr<int>
.
Sebaliknya, panggilan yang terlibat nullptr
tidak memiliki masalah. Ketika nullptr
diteruskan ke lockAndCall
, tipe untuk ptr
dideduksi menjadi std::nullptr_t
. Ketika ptr
diteruskan ke f3
, ada konversi implisit dari std::nullptr_t
ke int*
, karena std::nullptr_t
secara implisit mengkonversi ke semua jenis pointer.
Dianjurkan, Setiap kali Anda ingin merujuk ke pointer nol, gunakan nullptr, bukan 0 atau NULL
.
int
danvoid *
tidak akan memilihint
versi daripadavoid *
versi saat menggunakannullptr
.