Những lợi thế của việc sử dụng nullptr là gì?


163

Đoạn mã này về mặt khái niệm thực hiện điều tương tự cho ba con trỏ (khởi tạo con trỏ an toàn):

int* p1 = nullptr;
int* p2 = NULL;
int* p3 = 0;

Và như vậy, những ưu điểm của giao con trỏ là gì nullptrso với gán cho họ những giá trị NULLhay 0?


39
Đối với một điều, một chức năng quá tải lấy intvoid *sẽ không chọn intphiên bản trên void *phiên bản khi sử dụng nullptr.
chris

2
Vâng f(nullptr)là khác nhau từ f(NULL). Nhưng theo như mã trên (gán cho một biến cục bộ), tất cả ba con trỏ đều giống hệt nhau. Ưu điểm duy nhất là khả năng đọc mã.
balki

2
Tôi ủng hộ việc biến điều này thành một Câu hỏi thường gặp, @Prasoon. Cảm ơn!
sbi

1
NB NULL trong lịch sử không được đảm bảo là 0, nhưng là oc C99, theo cách tương tự, một byte không nhất thiết phải dài 8 bit và đúng và sai là các giá trị phụ thuộc kiến ​​trúc. Câu hỏi này tập trung vào nullptrbutthat sự khác biệt giữa 0 vàNULL
awiebe

Câu trả lời:


180

Trong mã đó, dường như không có một lợi thế. Nhưng hãy xem xét các chức năng quá tải sau đây:

void f(char const *ptr);
void f(int v);

f(NULL);  //which function will be called?

Hàm nào sẽ được gọi? Tất nhiên, ý định ở đây là để gọi f(char const *), nhưng trong thực tế f(int)sẽ được gọi! Đó là một vấn đề lớn 1 , phải không?

Vì vậy, giải pháp cho những vấn đề như vậy là sử dụng nullptr:

f(nullptr); //first function is called

Tất nhiên, đó không phải là lợi thế duy nhất của nullptr. Đây là một:

template<typename T, T *ptr>
struct something{};                     //primary template

template<>
struct something<nullptr_t, nullptr>{};  //partial specialization for nullptr

Vì trong mẫu, loại nullptrđược suy ra là nullptr_t, vì vậy bạn có thể viết cái này:

template<typename T>
void f(T *ptr);   //function to handle non-nullptr argument

void f(nullptr_t); //an overload to handle nullptr argument!!!

1. Trong C ++, NULLđược định nghĩa là #define NULL 0, vì vậy về cơ bản int, đó là lý do tại sao f(int)được gọi.


1
Như những gì Mehrdad đã nói, những loại quá tải này là khá hiếm. Có những lợi thế khác có liên quan nullptr? (Không. Tôi không đòi hỏi)
Mark Garcia

2
@MarkGarcia, Điều này có thể hữu ích: stackoverflow.com/questions/13665349/
chris

9
Chú thích của bạn có vẻ ngược. NULLđược tiêu chuẩn yêu cầu phải có một loại tích phân và đó là lý do tại sao nó thường được định nghĩa là 0hoặc 0L. Ngoài ra, tôi không chắc là tôi thích sự nullptr_tquá tải đó , vì nó chỉ bắt được các cuộc gọi nullptrchứ không phải với một con trỏ null thuộc loại khác (void*)0. Nhưng tôi có thể tin rằng nó có một số cách sử dụng, ngay cả khi tất cả những gì nó làm là giúp bạn xác định loại giữ chỗ có giá trị duy nhất của riêng bạn có nghĩa là "không".
Steve Jessop

1
Một lợi thế khác (dù được thừa nhận là nhỏ) có thể có nullptrgiá trị số được xác định rõ trong khi các hằng số con trỏ null thì không. Hằng số con trỏ null được chuyển đổi thành con trỏ null của loại đó (bất kể đó là gì). Yêu cầu hai con trỏ null cùng loại so sánh giống hệt nhau và chuyển đổi boolean biến một con trỏ null thành false. Không có gì khác là bắt buộc. Do đó, một trình biên dịch (ngớ ngẩn, nhưng có thể) có thể sử dụng ví dụ 0xabcdef1234hoặc một số số khác cho con trỏ null. Mặt khác, nullptrđược yêu cầu để chuyển đổi sang số không.
Damon

1
@DeadMG: Điều gì không đúng trong câu trả lời của tôi? Điều đó f(nullptr)sẽ không gọi chức năng dự định? Có nhiều hơn một động lực. Nhiều điều hữu ích khác có thể được phát hiện bởi chính các lập trình viên trong những năm tới. Vì vậy, bạn không thể nói rằng có chỉ có một cách sử dụng đúng của nullptr.
Nawaz

87

C ++ 11 giới thiệu nullptr, nó được gọi là Nullhằng số con trỏ và Nó cải thiện an toàn kiểugiải quyết các tình huống mơ hồ không giống như hằng số con trỏ null phụ thuộc thực hiện hiện có NULL. Để có thể hiểu những lợi thế của nullptr. trước tiên chúng ta cần hiểu những gì NULLvà những vấn đề liên quan đến nó là gì.


NULLChính xác là gì?

Pre C ++ 11 NULLđã được sử dụng để thể hiện một con trỏ không có giá trị hoặc con trỏ không trỏ đến bất cứ điều gì hợp lệ. Trái với khái niệm phổ biến NULLkhông phải là một từ khóa trong C ++ . Nó là một định danh được định nghĩa trong các tiêu đề thư viện tiêu chuẩn. Nói tóm lại, bạn không thể sử dụng NULLmà không bao gồm một số tiêu đề thư viện tiêu chuẩn. Xem xét chương trình mẫu :

int main()
{ 
    int *ptr = NULL;
    return 0;
}

Đầu ra:

prog.cpp: In function 'int main()':
prog.cpp:3:16: error: 'NULL' was not declared in this scope

Tiêu chuẩn C ++ định nghĩa NULL là một macro được xác định thực hiện được xác định trong các tệp tiêu đề thư viện tiêu chuẩn nhất định. Nguồn gốc của NULL là từ C và C ++ được thừa hưởng từ C. Tiêu chuẩn C định nghĩa NULL là 0hoặc (void *)0. Nhưng trong C ++ có một sự khác biệt tinh tế.

C ++ không thể chấp nhận thông số kỹ thuật này. Không giống như C, C ++ là một ngôn ngữ được gõ mạnh (C không yêu cầu truyền rõ ràng từ void*bất kỳ loại nào, trong khi C ++ bắt buộc phải sử dụng phân loại rõ ràng). Điều này làm cho định nghĩa về NULL được chỉ định bởi tiêu chuẩn C trở nên vô dụng trong nhiều biểu thức C ++. Ví dụ:

std::string * str = NULL;         //Case 1
void (A::*ptrFunc) () = &A::doSomething;
if (ptrFunc == NULL) {}           //Case 2

Nếu NULL được định nghĩa là (void *)0, cả hai biểu thức trên đều không hoạt động.

  • Trường hợp 1: Sẽ không biên dịch vì một dàn diễn viên tự động là cần thiết từ void *đến std::string.
  • Trường hợp 2: Sẽ không biên dịch vì cần truyền từ void *con trỏ đến hàm thành viên.

Vì vậy, không giống như C, C ++ Standard bắt buộc phải định nghĩa NULL là chữ số 0hoặc 0L.


Vì vậy, sự cần thiết cho một con trỏ null khác là gì khi chúng ta đã có NULL?

Mặc dù ủy ban Tiêu chuẩn C ++ đã đưa ra một định nghĩa NULL sẽ hoạt động cho C ++, định nghĩa này có một số vấn đề riêng. NULL đã làm việc đủ tốt cho hầu hết các kịch bản nhưng không phải tất cả. Nó đã cho kết quả đáng ngạc nhiên và sai lầm cho các kịch bản hiếm gặp nhất định. Ví dụ :

#include<iostream>
void doSomething(int)
{
    std::cout<<"In Int version";
}
void doSomething(char *)
{
   std::cout<<"In char* version";
}

int main()
{
    doSomething(NULL);
    return 0;
}

Đầu ra:

In Int version

Rõ ràng, ý định dường như là gọi phiên bản lấy char*làm đối số, nhưng khi đầu ra hiển thị chức năng lấy intphiên bản được gọi. Điều này là do NULL là một chữ số.

Hơn nữa, vì nó được xác định theo triển khai cho dù NULL là 0 hay 0L, nên có thể có nhiều nhầm lẫn trong độ phân giải quá tải chức năng.

Chương trình mẫu:

#include <cstddef>

void doSomething(int);
void doSomething(char *);

int main()
{
  doSomething(static_cast <char *>(0));    // Case 1
  doSomething(0);                          // Case 2
  doSomething(NULL)                        // Case 3
}

Phân tích đoạn trích trên:

  • Trường hợp 1: các cuộc gọi doSomething(char *)như mong đợi.
  • Trường hợp 2: các cuộc gọi doSomething(int)nhưng có thể char*phiên bản được mong muốn vì 0IS cũng là một con trỏ null.
  • Trường hợp 3: Nếu NULLđược định nghĩa là 0, các cuộc gọi doSomething(int)khi có thể doSomething(char *)được dự định, có thể dẫn đến lỗi logic khi chạy. Nếu NULLđược định nghĩa là 0L, cuộc gọi không rõ ràng và dẫn đến lỗi biên dịch.

Vì vậy, tùy thuộc vào việc thực hiện, cùng một mã có thể đưa ra các kết quả khác nhau, điều này rõ ràng là không mong muốn. Đương nhiên, ủy ban tiêu chuẩn C ++ muốn sửa lỗi này và đó là động lực chính cho nullptr.


Vì vậy, những gì là nullptrvà làm thế nào để tránh các vấn đề của NULL?

C ++ 11 giới thiệu một từ khóa mới nullptrđể phục vụ như hằng số con trỏ null. Không giống như NULL, hành vi của nó không được xác định theo triển khai. Nó không phải là một macro nhưng nó có loại riêng của nó. nullptr có loại std::nullptr_t. C ++ 11 định nghĩa một cách thích hợp các thuộc tính cho nullptr để tránh các nhược điểm của NULL. Để tóm tắt các thuộc tính của nó:

Thuộc tính 1: nó có loại riêng std::nullptr_t
Thuộc tính 2: nó hoàn toàn có thể chuyển đổi và có thể so sánh với bất kỳ loại con trỏ hoặc loại con trỏ thành thành viên nào, nhưng
Thuộc tính 3: nó không thể chuyển đổi hoặc so sánh với các loại tích phân, ngoại trừ bool.

Hãy xem xét ví dụ sau:

#include<iostream>
void doSomething(int)
{
    std::cout<<"In Int version";
}
void doSomething(char *)
{
   std::cout<<"In char* version";
}

int main()
{
    char *pc = nullptr;      // Case 1
    int i = nullptr;         // Case 2
    bool flag = nullptr;     // Case 3

    doSomething(nullptr);    // Case 4
    return 0;
}

Trong chương trình trên,

  • Trường hợp 1: OK - Tài sản 2
  • Trường hợp 2: Không ổn - Tài sản 3
  • Trường hợp 3: OK - Tài sản 3
  • Trường hợp 4: Không nhầm lẫn - char *Phiên bản cuộc gọi , Thuộc tính 2 & 3

Do đó, việc giới thiệu nullptr tránh được tất cả các vấn đề của NULL cũ.

Bạn nên sử dụng như thế nào và ở đâu nullptr?

Nguyên tắc chung cho C ++ 11 chỉ đơn giản là bắt đầu sử dụng nullptrbất cứ khi nào bạn sử dụng NULL trong quá khứ.


Tài liệu tham khảo tiêu chuẩn:

Tiêu chuẩn C ++ 11: C.3.2.4 Macro NULL
C ++ 11 Tiêu chuẩn: 18.2 Các loại
C ++ 11 Tiêu chuẩn: 4.10 Chuyển đổi con trỏ
C99 Tiêu chuẩn: 6.3.2.3 Con trỏ


Tôi đã thực hành lời khuyên cuối cùng của bạn kể từ khi tôi biết nullptr, mặc dù tôi không biết nó thực sự khác biệt gì với mã của tôi. Cảm ơn câu trả lời tuyệt vời và đặc biệt là cho những nỗ lực. Mang lại cho tôi rất nhiều ánh sáng về chủ đề này.
Mark Garcia

"trong các tệp tiêu đề thư viện tiêu chuẩn nhất định." -> tại sao không chỉ viết "cstddef" từ đầu?
mxmlnkn

Tại sao chúng ta nên cho phép nullptr có thể chuyển đổi thành loại bool? Bạn có thể vui lòng giải thích thêm?
Robert Wang

... được sử dụng để biểu thị một con trỏ không có giá trị ... Các biến luôn có giá trị. Nó có thể là nhiễu, hoặc 0xccccc...., nhưng, một biến không có giá trị là một mâu thuẫn cố hữu.
3Dave

"Trường hợp 3: OK - Thuộc tính 3" (dòng bool flag = nullptr;). Không, không ổn, tôi gặp lỗi sau tại thời gian biên dịch với g ++ 6:error: converting to ‘bool’ from ‘std::nullptr_t’ requires direct-initialization [-fpermissive]
Georg

23

Động lực thực sự ở đây là chuyển tiếp hoàn hảo .

Xem xét:

void f(int* p);
template<typename T> void forward(T&& t) {
    f(std::forward<T>(t));
}
int main() {
    forward(0); // FAIL
}

Nói một cách đơn giản, 0 là một giá trị đặc biệt , nhưng các giá trị không thể truyền qua các loại chỉ hệ thống có thể. Các chức năng chuyển tiếp là rất cần thiết và 0 không thể đối phó với chúng. Vì vậy, nó là hoàn toàn cần thiết để giới thiệu nullptr, trong đó loại là đặc biệt, và loại thực sự có thể tuyên truyền. Trên thực tế, nhóm MSVC đã phải giới thiệu nullptrtrước thời hạn sau khi họ triển khai các tài liệu tham khảo giá trị và sau đó tự mình phát hiện ra cạm bẫy này.

Có một vài trường hợp góc khác nullptrcó thể làm cho cuộc sống dễ dàng hơn - nhưng đó không phải là trường hợp cốt lõi, vì một diễn viên có thể giải quyết những vấn đề này. Xem xét

void f(int);
void f(int*);
int main() { f(0); f(nullptr); }

Gọi hai quá tải riêng biệt. Ngoài ra, hãy xem xét

void f(int*);
void f(long*);
int main() { f(0); }

Điều này là mơ hồ. Nhưng, với nullptr, bạn có thể cung cấp

void f(std::nullptr_t)
int main() { f(nullptr); }

7
Buồn cười. Một nửa câu trả lời giống như hai câu trả lời khác mà theo bạn là câu trả lời "khá không chính xác" !!!
Nawaz

Vấn đề chuyển tiếp cũng có thể được giải quyết với một diễn viên. forward((int*)0)làm. Tui bỏ lỡ điều gì vậy?
jcsahnwaldt phục hồi Monica

5

Khái niệm cơ bản về nullptr

std::nullptr_tlà 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 nullptrhơ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 lockAndCallthay vì nhiều định nghĩa lockAndCallF1, lockAndCallF2 & lockAndCallF3cho 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ố f1mong đợ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 lockAndCall0 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 NULLvề 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 ptrloạ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 nullptrkhô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_tsang int*, bởi vì std::nullptr_thoà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.


4

Không có lợi thế trực tiếp của việc có nullptrtrong cách bạn đã đưa ra các ví dụ.
Nhưng hãy xem xét một tình huống trong đó bạn có 2 chức năng có cùng tên; 1 mất intvà một khácint*

void foo(int);
void foo(int*);

Nếu bạn muốn gọi foo(int*)bằng cách chuyển NULL, thì cách là:

foo((int*)0); // note: foo(NULL) means foo(0)

nullptrlàm cho nó dễ dàng và trực quan hơn :

foo(nullptr);

Liên kết bổ sung từ trang web của Bjarne.
Không liên quan nhưng lưu ý bên C ++ 11:

auto p = 0; // makes auto as int
auto p = nullptr; // makes auto as decltype(nullptr)

3
Để tham khảo, decltype(nullptr)std::nullptr_t.
chris

2
@MarkGarcia, Đó là một loại đầy đủ theo như tôi biết.
chris

5
@MarkGarcia, Đó là một câu hỏi thú vị. cppreference có : typedef decltype(nullptr) nullptr_t;. Tôi đoán tôi có thể nhìn vào tiêu chuẩn. À, đã tìm thấy nó: Lưu ý: std :: nullptr_t là một loại khác biệt không phải là loại con trỏ cũng không phải là con trỏ đến loại thành viên; thay vào đó, một giá trị của loại này là hằng số con trỏ null và có thể được chuyển đổi thành giá trị con trỏ null hoặc giá trị con trỏ null.
chris

2
@DeadMG: Có nhiều hơn một động lực. Nhiều điều hữu ích khác có thể được phát hiện bởi chính các lập trình viên trong những năm tới. Vì vậy, bạn không thể nói rằng có chỉ có một cách sử dụng đúng của nullptr.
Nawaz

2
@DeadMG: Nhưng bạn đã nói câu trả lời này là "khá không chính xác" đơn giản vì nó không nói về "động lực thực sự" mà bạn đã nói trong câu trả lời của mình. Không chỉ vậy, câu trả lời này (và của tôi cũng vậy) đã nhận được một downvote từ bạn.
Nawaz

4

Như những người khác đã nói, lợi thế chính của nó nằm ở sự quá tải. Và trong khi intquá tải rõ ràng so với con trỏ có thể hiếm gặp, hãy xem xét các hàm thư viện tiêu chuẩn như std::fill(đã cắn tôi nhiều lần trong C ++ 03):

MyClass *arr[4];
std::fill_n(arr, 4, NULL);

Không biên dịch : Cannot convert int to MyClass*.


2

IMO quan trọng hơn các vấn đề quá tải đó: trong các cấu trúc mẫu được lồng sâu, thật khó để không bị mất dấu các loại và đưa ra chữ ký rõ ràng là một nỗ lực khá lớn. Vì vậy, đối với mọi thứ bạn sử dụng, càng tập trung chính xác vào mục đích đã định thì càng tốt, nó sẽ giảm nhu cầu về chữ ký rõ ràng và cho phép trình biên dịch tạo ra các thông báo lỗi sâu sắc hơn khi có sự cố.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.