聊一聊c++中指針為空的三種寫法 ----->NULL, 0, nullptr


看到同事用了一下nullptr.不是很了解這方面東東,找個帖子學習學習  http://www.cppblog.com/airtrack/archive/2012/09/16/190828.aspx

NULL:

NULL是c語言的東西,定義處: #define NULL ((void *)0)

我們可以寫  int* i = NULL, foo_t* pObj = NULL.

NULL實際上是一個void *的指針,然后吧void *指針賦值給int *和foo_t *的時候,會隱式轉換成相應的類型。而如果換做一個C++編譯器來編譯的話是要出錯的,因為C++是強類型的,void *是不能隱式轉換成其他指針類型的,所以通常情況下,編譯器提供的頭文件會這樣定義NULL:

#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif

C++ 的0

因為C++中不能將void *類型的指針隱式轉換成其他指針類型,而又為了解決空指針的問題,所以C++中引入0來表示空指針,這樣就有了類似上面的代碼來定義NULL。實際上C++的書都會推薦說C++中更習慣使用0來表示空指針而不是NULL,盡管NULL在C++編譯器下就是0。為什么C++的書都推薦使用0而不是NULL來表示空指針呢?我們看一個例子:

 1 //foo.h
 2 void bar(type1 a, type2* b);
 3 
 4 //foo.h中的bar函數在a.cpp和b.cpp中都被調用了.
 5 
 6 //a.cpp
 7 .....
 8 bar(a,b);
 9 .....
10 
11 //b.cpp
12 .....
13 bar(a, 0);
14 .....
15 
16 //現在,上面的代碼都能編譯運行.但是突然某天我們要功能擴展,需要對bar函數擴展,我們使用了重載  foo.h變成如下:
17 void bar(sometype1 a, sometype2 *b);
18 void bar(sometype1 a, int i);
19 
20 //這個時候就危險了.因為a.cpp和b.cpp中的調用代碼這個時候就不能按照期望的運行了。但是我們很快就會發現b.cpp中的0是整數,也就是在overload resolution的時候,我們知道它調用的是void bar(sometype1 a, int i)這個重載函數,於是我們可以做出如下修改讓代碼按照期望運行:
21 
22 bar(a, static_cast<type2 *>(0));
23 
24 //我知道,如果我們一開始就有bar的這兩個重載函數的話,我們會在一開始就想辦法避免這個問題(不使用重載)或者我們寫出正確的調用代碼,然而后面的這個重載函數或許是我們幾個月或者很長一段時間后加上的話,那我們出錯的可能性就會加大了不少。貌似我們現在說道的這些跟C++通常使用0來表示空指針沒什么關系,好吧,假設我們的調用代碼是這樣的:
25 //foo.h
26 void bar(type1 a, type2 *b);
27 
28 //a.cpp
29 ......
30 bar(a, b);
31 ......
32 //b.cpp
33 .....
34 bar(a,NULL);
35 .....
36 
37 //當bar的重載函數在后面加上來了之后,我們會發現出錯了,但是出錯的時候,我們找到b.cpp中的調用代碼也很快可能忽略過去了,因為我們用的是NULL空指針啊,應該是調用的void bar(type1 a, type2 *b)這個重載函數啊。實際上NULL在C++中就是0,寫NULL這個反而會讓你沒那么警覺,因為NULL不夠“明顯”,而這里如果是使用0來表示空指針,那就會夠“明顯”,因為0是空指針,它更是一個整形常量。
38 
39 在C++中,使用0來做為空指針會比使用NULL來做空指針會讓你更加警覺。

C++ 11的nullptr

 1 //雖然上面我們說明了0比NULL可以讓我們更加警覺,但是我們並沒有避免這個問題。這個時候C++ 11的nullptr就很好的解決了這個問題,我們在C++ 11中使用nullptr來表示空指針,這樣最早的代碼是這樣的
 2 
 3 //foo.h
 4 
 5 void bar(type1 a, type2* b);
 6 -------------------------------------------
 7 | //a.cpp          |         //b.cpp        |
 8 | ....             |             .......    |
 9 | bar(a,b);        |        bar(a, nullptr);|
10 | .......          |             ........   | 
11 ---------------------------------------------
12 在我們后來把bar的重載加上了之后,代碼是這樣:
13 
14 //foo.h
15 void bar(type1 a, type2 *b);
16 void bar(type1 a, int i);
17 
18 //a.cpp                    //b.cpp
19 ....                          .....
20 bar(a,b);                bar(a,nullptr);
21 ....                          ......
22 
23 //這時候,我們的代碼還是能夠如期的正確運行.
24 
25 //在沒有C++11的nullptr的時候,我們應該怎么解決避免這個問題呢?我們可以自己實現一個nullptr
26 
27 const
28 class nullptr_t
29 {
30 public:
31     template<class T>
32     inline operator T*() const
33         { return 0; }
34 
35     template<class C, class T>
36     inline operator T C::*() const
37         { return 0; }
38  
39 private:
40     void operator&() const;
41 } nullptr = {};


和小伙伴都驚了個呆了啊...上面自己實現的nullptr_t類完全看不懂的樣子,得一句一句的分析分析啊...

 1 const                       //參見下文解釋1
 2 class nullptr_t
 3 {
 4 public:
 5       template<class T>                       
 6             inline operator T* () const         //參見下文解釋2
 7                        {
 8                               return 0;
 9                         }
10        
11       template<class C, class T>
12               inline operator T C::*() const      //參見下文解釋3
13                         {
14                                return 0;
15                          }
16 
17 private:
18        void operator& () const;       //參見下文解釋4
19  
20 }nullptr = {};   //參見下文解釋5

解釋1 : 在類前面的const是什么!!

 1 //類前面的const 是修飾 類后面定義的對象的.
 2 
 3 const
 4 class A
 5 {
 6     public:
 7         int i;
 8 }a,b;
 9 
10 //等價於
11 class A
12 {
13     public:
14         int i;
15 };
16 
17 const A a;
18 const A b;

解釋2 : 這個模板函數是什么?

 1 template <class T>
 2     inline operator T* () const
 3         { 
 4              return 0;
 5          }
 6 
 7 //上面的模板函數是在重載 類型轉換運算符  (跟 重載 operator = 一樣都是在重載運算符).
 8 //我們知道重載運算符是要帶返回值的,例如下面的類重載等號 是要返回A&的  
 9 class A
10 {
11 public:
12      A& operater = (const A& Obj)
13           {
14               i = Obj.i;
15               j = Obj.j;
16            }
17 private:
18     int i;
19     int j;
20 };
21 //但是呢.重載 類型轉換運算符比較特殊. (c++ primer,可以查“用戶自定義類型轉換符”找到講解) 規定為:轉換函數不能寫返回類型(規定的),返回的類型就是 operator 后面跟的類型
22 所以:
23  inline operator T* () const
24 ---------------------------------------------
25 這是一個類型轉換函數,把A類型(這里是return 0 的 0的類型)轉換成 T*的類型
26 inline 表示內聯函數,寫不寫無所謂。
27 operator 代表重載某種操作 operator T* () 就是重載類型轉換 
28 const 表示 成員變量是只讀,不能改。
29 
30 return _data; // 有返回值,返回值類型是operator后面的T*類型。

解釋3 : 這個模板函數又是什么???

 

 

解釋4:  只寫函數聲明,不寫函數實現真的可以么??

事實證明是可以的,只有沒人調用這個函數是可以編譯運行成功的..但是如果有人調用的話編譯就會報錯了.

解釋5: 類對象 = {} 中的 ={}是什么??

= {} 是c++11的語法,代表給這個變量初始化

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM