史上最も明白なNULL、0、nullptrの区別分析(先生がN編も分からなかったことを話している)が、今日やっと分かった.

5227 ワード

CのNULL
C言語では、NULLを使用して空のポインタを表します.つまり、次のコードを書くことができます.
int *i = NULL;
foo_t *f = NULL;

実際、C言語では、NULLは通常次のように定義されます.
#define NULL ((void *)0)

つまりNULLは実際にはvoid*のポインタであり、void*のポインタはint*とfoo_に割り当てられます.t*のポインタの場合、暗黙的に対応するタイプに変換されます.一方、C++コンパイラに変換してコンパイルするとエラーが発生します.C++は強いタイプであり、void*は他のポインタタイプに暗黙的に変換できないため、コンパイラが提供するヘッダファイルはNULLを定義します.
#ifdef __cplusplus ---  :cpp c++   
#define NULL 0
#else
#define NULL ((void *)0)
#endif

C++の0
C++ではvoid*タイプのポインタを暗黙的に他のポインタタイプに変換することができず、空のポインタを決定する問題を理解するために、C++に0を導入して空のポインタ(注:0で示すか、欠陥があるか)を表し、上記のコードのようにNULLを定義します.実際にはC++の本はC++の中でNULLではなく空のポインタを表すのに慣れているとお勧めしますが、NULLはC++コンパイラの下では0です.なぜC++の本はNULLではなく0を使って空のポインタを表すことをお勧めしますか?例を見てみましょう
foo.hファイルには関数が宣言されています.
void bar(sometype1 a, sometype2 *b);

この関数はa.cpp、b.cppで呼び出され、それぞれ:
a.cpp:
bar(a, b);

b.cpp:
bar(a, 0);

 
はい、これらのコードは正常に完璧にコンパイルされています.しかし、突然ある時、私たちの機能は拡張して、bar関数を拡張する必要があります.私たちはリロードを使用して、今foo.hの声明は以下の通りである.
void bar(sometype1 a, sometype2 *b);
void bar(sometype1 a, int i);

この時は危険ですが、a.cppとb.cppの呼び出しコードはこの時は期待通りに実行できません.しかし、b.cppの0は整数であることがすぐにわかります.つまり、overload resolutionのとき、void bar(sometype 1 a,int i)というリロード関数が呼び出されていることを知っています.そこで、コードを所望のように実行するように修正することができます.
bar(a, static_cast(0));  ---               ,        

私は知っていて、もし私达が最初からbarのこの2つのリロード関数があるならば、私达は最初からこの问题を避ける(リロードを使わない)あるいは私达は正しい呼び出しコードを书くことができて、しかし后のこのリロード関数は私达が数ヶ月あるいはとても长い时间の后でプラスするかもしれないならば、私达の间违いの可能性は多くなります.私たちが今言っているのは、C++が通常0を使用して空のポインタを表すこととは関係ないようです.では、呼び出しコードが次のようになっているとします.
foo.h
void bar(sometype1 a, sometype2 *b);

a.cpp
bar(a, b);

b.cpp
bar(a, NULL);

barのリロード関数が後で加算されると、エラーが検出されますが、エラーが発生した場合、b.cppの呼び出しコードもすぐに無視される可能性があります.NULL空ポインタを使用しているので、呼び出したvoid bar(sometype 1 a,sometype 2*b)というリロード関数でしょう.実はNULLはC++の中で0で、NULLを書くのはかえってあなたをそんなに警戒させないで、NULLが“明らか”ではありませんため、ここで0を使って空のポインタを表すならば、それは“明らか”で、0が空のポインタなため、それは更に1つの整形の定数です.
C++では、NULLを使って空のポインタを作るよりも0を使って空のポインタを作るほうが警戒感があります.
 
C++11のnullptr
0がNULLよりも警戒できることを説明したが、私たちはこの問題を避けなかった.このときC++11のnullptrはこの問題をうまく解決し,我々はC++11でnullptrを用いて空のポインタを表すようにし,このような最初のコードはこのようにして,
foo.h
void bar(sometype1 a, sometype2 *b);

a.cpp
bar(a, b);

b.cpp
bar(a, nullptr);

私たちがbarのリロードを追加した後、コードはこうです.
foo.h
void bar(sometype1 a, sometype2 *b);
void bar(sometype1 a, int i);

a.cpp
bar(a, b);

b.cpp
bar(a, nullptr);

この場合、私たちのコードは予想通りに正しく動作します.
C++11のnullptrがない場合、私たちはどのようにこの問題を回避しますか?私たちは自分で一つを実現することができます(『Imperfect C++』には一つの実現があります):
const
class nullptr_t
{
public:
    template<class T>
    inline operator T*() const
        { return 0; }

    template<class C, class T>
    inline operator T C::*() const
        { return 0; }
 
private:
    void operator&() const;
} nullptr = {};

これは何度も議論されていますが、C++11が普及する前に、私たちはどのように問題を避けるか、どのようにすぐに問題を見つけるかを知る必要があると思います.
 
-----結論:nullptrを用いてオブジェクトを初期化すれば,0ポインタの二義性の問題を回避できる. cocos2d-x3.0はc++11の新しい特性を採用しました...c++二春が来た...
 
転載先:https://www.cnblogs.com/porter/p/3611718.html