Nullptr'in temelleri
std::nullptr_t
null işaretçi değişmezinin türü, nullptr. Bu bir tür ön değer / değerdir std::nullptr_t
. Herhangi bir işaretçi türünün nullptr değerinden null işaretçi değerine örtülü dönüştürmeler vardır.
Değişmez 0, bir işaretçi değil, int'tir. C ++ kendisini yalnızca bir işaretçinin kullanılabileceği bir bağlamda 0'a bakarken bulursa, 0'ı küstahça boş gösterici olarak yorumlar, ancak bu bir geri dönüş konumudur. C ++ 'ın birincil ilkesi 0 bir işaretçi değil, bir int olmasıdır.
Avantaj 1 - İşaretçi ve integral türlerine aşırı yüklenirken belirsizliği ortadan kaldırın
C ++ 98'de bunun birincil anlamı, işaretçi ve integral türlerine aşırı yüklenmenin sürprizlere yol açabileceğiydi. Bu tür aşırı yüklere 0 veya NULL iletilmesi, hiçbir zaman işaretçi aşırı yükü olarak adlandırılmaz:
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*)
Bu çağrı ile ilgili ilginç olan şey, kaynak kodun görünen anlamı (“NULL-boş gösterici ile eğlenceye çağırıyorum”) ve gerçek anlamı (“bir tür tamsayı ile eğlenceye çağırıyorum - boş değil) Işaretçi").
nullptr'un avantajı, ayrılmaz bir türü olmamasıdır. Nullptr ile aşırı yüklenmiş işlevi eğlence olarak adlandırmak, void * aşırı yükünü (yani, işaretçi aşırı yüklenmesi) çağırır, çünkü nullptr tümleşik bir şey olarak görüntülenemez:
fun(nullptr); // calls fun(void*) overload
0 veya NULL yerine nullptr kullanmak aşırı çözünürlük sürprizlerini önler.
Bir diğer avantajı nullptr
over NULL(0)
dönüş türü için otomatik kullanırken
Örneğin, bunun bir kod tabanıyla karşılaştığınızı varsayalım:
auto result = findRecord( /* arguments */ );
if (result == 0) {
....
}
FindRecord'un neyi döndürdüğünü bilmiyorsanız (veya kolayca bulamıyorsanız), sonucun bir işaretçi türü veya integral türü olup olmadığı net olmayabilir. Sonuçta, 0 (hangi sonuca karşı test edilir) her iki şekilde de gidebilir. Diğer taraftan aşağıdakileri görürseniz,
auto result = findRecord( /* arguments */ );
if (result == nullptr) {
...
}
belirsizlik yoktur: sonuç bir işaretçi türü olmalıdır.
Avantaj 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;
}
Yukarıda program derleme ve başarıyla yürütülür ancak lockAndCallF1, lockAndCallF2 & lockAndCallF3 yedek kodu vardır. Tüm bunlar için şablon yazabilirsek, böyle bir kod yazmak üzücü lockAndCallF1, lockAndCallF2 & lockAndCallF3
. Böylece şablon ile genelleştirilebilir. lockAndCall
Çoklu tanım yerine şablon fonksiyonu yazdımlockAndCallF1, lockAndCallF2 & lockAndCallF3
Yedek kod için .
Kod aşağıdaki gibi yeniden faktörlendirilir:
#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;
}
Derleme için neden başarısız Detay analizi lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
için değillockAndCall(f3, f3m, nullptr)
Neden derleme lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
başarısız oldu?
Sorun, lockAndCall öğesine 0 iletildiğinde, şablon türü kesinti türünü bulmak için devreye girmesidir. 0 türü int şeklindedir, bu nedenle lockAndCall çağrısının örneğinin içindeki ptr parametresinin türü budur. Ne yazık ki, bu lockAndCall içinde func için çağrıda, bir int geçiriliyor ve bu beklenen std::shared_ptr<int>
parametre ile uyumlu olmadığı anlamına gelir f1
. Çağrısında geçirilen 0 lockAndCall
, boş bir işaretçiyi temsil etmeyi amaçladı, ancak gerçekte geçen şey int idi. Bu int'i f1'e a olarak aktarmaya çalışmak std::shared_ptr<int>
bir tür hatadır. lockAndCall
Şablonun içinde bir int gerektiren bir işleve int iletildiği için 0 ile çağrısı başarısız olur std::shared_ptr<int>
.
Çağrı içeren analiz NULL
esasen aynıdır. 'E NULL
iletildiğinde lockAndCall
, ptr parametresi için bir integral türü çıkarılır ve a'yı almayı bekleyen ptr
—an int veya int benzeri bir tür-- iletildiğinde bir tür hatası oluşur .f2
std::unique_ptr<int>
Aksine, çağrı içeren nullptr
herhangi bir sorun yoktur. Ne zaman nullptr
iletilir lockAndCall
, için türü ptr
çıkarılır std::nullptr_t
. Ne zaman ptr
geçirilir f3
, bir örtük dönüşüm gerçekleşmesi std::nullptr_t
için int*
, çünkü std::nullptr_t
dolaylı dönüştürür tüm işaretçi türlerine.
Bir boş göstericiye başvurmak istediğinizde, 0 veya nullptr kullanın NULL
.
int
vevoid *
seçim olmayacaktırint
üzerinde sürümünüvoid *
kullanırken sürümünullptr
.