Khái niệm cơ bản về nullptr
std::nullptr_t
là loại con trỏ null bằng chữ, nullptr. Nó là một giá trị / giá trị của loại std::nullptr_t
. Tồn tại các chuyển đổi ngầm định từ nullptr sang giá trị con trỏ null của bất kỳ loại con trỏ nào.
Chữ 0 là số nguyên chứ không phải con trỏ. Nếu C ++ thấy mình đang nhìn 0 trong bối cảnh chỉ có thể sử dụng một con trỏ, thì nó sẽ hiểu một cách miễn cưỡng 0 là một con trỏ rỗng, nhưng đó là một vị trí dự phòng. Chính sách chính của C ++ là 0 là số nguyên chứ không phải con trỏ.
Ưu điểm 1 - Loại bỏ sự mơ hồ khi quá tải trên các loại con trỏ và tích phân
Trong C ++ 98, hàm ý chính của điều này là quá tải trên con trỏ và các kiểu tích phân có thể dẫn đến những bất ngờ. Truyền 0 hoặc NULL cho các tình trạng quá tải như vậy không bao giờ được gọi là quá tải con trỏ:
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*)
Điều thú vị của cuộc gọi đó là sự mâu thuẫn giữa ý nghĩa rõ ràng của mã nguồn (Cạn tôi đang gọi vui với NULL-con trỏ null) và ý nghĩa thực sự của nó con trỏ hướng).
Ưu điểm của nullptr là nó không có loại tích phân. Gọi hàm quá tải là niềm vui với nullptr gọi void * quá tải (nghĩa là quá tải con trỏ), vì nullptr không thể được xem là bất cứ thứ gì tách rời:
fun(nullptr); // calls fun(void*) overload
Sử dụng nullptr thay vì 0 hoặc NULL để tránh những bất ngờ về độ phân giải quá tải.
Một ưu điểm khác của nullptr
hơn NULL(0)
khi sử dụng ô tô cho kiểu trả về
Ví dụ: giả sử bạn gặp điều này trong cơ sở mã:
auto result = findRecord( /* arguments */ );
if (result == 0) {
....
}
Nếu bạn không biết (hoặc không thể dễ dàng tìm ra) những gì findRecord trả về, có thể không rõ liệu kết quả là loại con trỏ hay loại tích phân. Rốt cuộc, 0 (kết quả được kiểm tra đối với) có thể đi theo bất kỳ cách nào. Nếu bạn thấy những điều sau đây, mặt khác,
auto result = findRecord( /* arguments */ );
if (result == nullptr) {
...
}
không có sự mơ hồ: kết quả phải là một loại con trỏ.
Lợi thế 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;
}
Chương trình trên đã biên dịch và thực hiện thành công nhưng lockAndCallF1, lockAndCallF2 & lockAndCallF3 có mã dự phòng. Thật đáng tiếc khi viết mã như thế này nếu chúng ta có thể viết mẫu cho tất cả những điều này lockAndCallF1, lockAndCallF2 & lockAndCallF3
. Vì vậy, nó có thể được khái quát với mẫu. Tôi đã viết hàm mẫu lockAndCall
thay vì nhiều định nghĩa lockAndCallF1, lockAndCallF2 & lockAndCallF3
cho mã dự phòng.
Mã được bao gồm lại như sau:
#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;
}
Phân tích chi tiết tại sao việc biên dịch thất bại vì lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
không cholockAndCall(f3, f3m, nullptr)
Tại sao biên dịch lockAndCall(f1, f1m, 0) & lockAndCall(f3, f3m, nullptr)
thất bại?
Vấn đề là khi 0 được chuyển đến lockAndCall, khấu trừ kiểu mẫu sẽ bắt đầu để tìm ra loại của nó. Kiểu 0 là int, vì vậy đó là loại tham số ptr bên trong việc khởi tạo cuộc gọi này tới lockAndCall. Thật không may, điều này có nghĩa là trong lệnh gọi func bên trong lockAndCall, một int đang được thông qua và điều đó không tương thích với std::shared_ptr<int>
tham số f1
mong đợi. Số 0 được truyền trong lệnh gọi lockAndCall
được dự định đại diện cho một con trỏ null, nhưng những gì thực sự được truyền là int. Cố gắng chuyển int này thành F1 std::shared_ptr<int>
là một lỗi loại. Cuộc gọi đến lockAndCall
0 không thành công vì bên trong khuôn mẫu, một int đang được chuyển đến một hàm yêu cầu a std::shared_ptr<int>
.
Các phân tích cho các cuộc gọi liên quan NULL
về cơ bản là giống nhau. Khi NULL
được truyền đến lockAndCall
, một loại tích phân được suy ra cho tham số ptr và một lỗi loại xảy ra khi ptr
loạiananan hoặc kiểu int giống như được chuyển đến f2
, dự kiến sẽ nhận được a std::unique_ptr<int>
.
Ngược lại, cuộc gọi liên quan nullptr
không có rắc rối. Khi nullptr
được truyền vào lockAndCall
, loại cho ptr
được suy ra là std::nullptr_t
. Khi ptr
được chuyển đến f3
, có một chuyển đổi ngầm định từ std::nullptr_t
sang int*
, bởi vì std::nullptr_t
hoàn toàn chuyển đổi sang tất cả các loại con trỏ.
Đó là khuyến cáo, Bất cứ khi nào bạn muốn tham chiếu đến một con trỏ null, hãy sử dụng nullptr, không phải 0 hoặc NULL
.
int
vàvoid *
sẽ không chọnint
phiên bản trênvoid *
phiên bản khi sử dụngnullptr
.