浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第6课 nullptr_t和nullptr

Posted on 2019-07-25 16:48  浅墨浓香  阅读(3793)  评论(0编辑  收藏  举报

一. nullptr与nullptr_t

(一)nullptr_t是一种数据类型而nullptr是该类型的一个实例。通常情况下,也可以通过nullptr_t类型创建另一个新的实例。

(二)所有定义为nullptr_t类型的数据都是等价的,行为也是完全一致的。

(三)std::nullptr_t类型,并不是指针类型,但可以隐式转换成任意一个指针类型(注意不能转换为非指针类型,强转也不行)。

(四)nullptr_t类型的数据不适用于算术运算表达式。但可以用于关系运算表达式(仅能与nullptr_t类型数据或指针类型数据进行比较,当且仅当关系运算符为==、<=、>=等时)

【编程实验】nullptr与nullptr_t的关系

#include <iostream>
#include <vector>
#include <iomanip>  //for setbase;
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr;

//辅助类模板,用于打印T的类型
template <typename T>
void printType(string s)
{
    cout << s << " = " << type_id_with_cvr<T>().pretty_name() << endl;
}

//4. 用于测试nullptr并不是指针类型
template<typename T>
void func_ptr(T* t) {}

template<typename T>
void func_value(T t) {}

int main()
{
    //1. 查看nullptr的类型
    printType<decltype(nullptr)>("nullptr的类型: ");  //std::nullptr_t
    cout << "sizeof(nullptr) = " << sizeof(nullptr) << endl;

    //2. nullptr可以隐式转换为任何指针!
    void* vptr = nullptr;
    char* cptr = nullptr;

    //3. 使用关系运算符进行比较nullptr与nullptr_t类型的变量
    //3.1 nullptr支持关系运算
    nullptr_t my_nullptr = nullptr; //通过nullptr_t定义新的实例(vc下必须初始化,但g++默认下己初始化为nullptr)

    (nullptr == my_nullptr) ? (cout << "newptr == nullptr" << endl) : (cout << "newptr != nullptr" << endl);
    (nullptr < my_nullptr) ? (cout << "newptr < nullptr" << endl) : (cout << "newptr !< nullptr" << endl);

    //3.2 nullptr不支持算术运算
    //nullptr += 1; //nullptr是个常量
    //nullptr * 5;

    //4. nullptr_t并不是指针类型(尽管看起来、用起来都像指针类型)
    func_ptr((float*)nullptr); //ok, T = float。nullptr可以转为任意类型的指针
    //func_ptr(nullptr);       //编译失败,nullptr的类型是nullptr_t,而不是指针类型
                               //编译器并不会“智能”地推导成某种类型的指针(含void*)

    func_value(0);           // T = int;
    func_value(nullptr);     //T = nullptr_t;
    func_value((float*)nullptr);  //T = float*

    return 0;
}
/*输出结果
nullptr的类型:  = std::nullptr_t
sizeof(nullptr) = 4
newptr == nullptr
newptr !< nullptr
*/

二、nullptr与NULL

(一)nullptr与NULL的区别

  1. NULL是一个宏定义,C++中通常将其定义为0编译器总是优先把它当作一个整型常量(C标准下定义为(void*)0)。

  2. nullptr是一个编译期常量,其类型为nullptr_t它既不是整型类型,也不是指针类型

  3. 在模板推导中,nullptr被推导为nullptr_t类型,仍可隐式转为指针。但0或NULL则会被推导为整型类型

  4.要避免在整型和指针间进行函数重载。因为NULL会被匹配到整型形参版本的函数,而不是预期的指针版本。

(二)nullptr与(void*)0的区别

  1. nullptr到任何指针的转换是隐式的。(尽管nullptr不是指针类型,但仍可当指针使用)

  2.(void*)0只是一个强制转换表达式,其返回void*指针类型,只能经过类型转换到其他指针才能用

【编程实验】nullptr与NULL的区别

#include <iostream>
#include<memory>
#include <mutex>
#include <iomanip>  //for setbase;
using namespace std;

//3. 避免整型和指针间的重载
void f(int)
{
    cout <<"invoke f(int)" << endl;
}

void f(void*)
{
    cout << "invoke f(void*)" << endl;
}

//4. nullptr在模板中的表现
class Widget{};
int f1(std::shared_ptr<Widget> spw) { return 0; }
double f2(std::unique_ptr<Widget> upw) { return 0; }
bool f3(Widget* pw) { return true; }
using MuxGuard = std::lock_guard<std::mutex>;

template<typename Func, typename Mux, typename Ptr>
decltype(auto) lockAndCall(Func func, Mux& mux, Ptr ptr) //C++14
{
    MuxGuard guard(mux);
    return func(ptr);
}

int main()
{
    //1. nullptr是一个右值常量
    nullptr_t my_nullptr;

    cout <<"&my_nullptr = "<< setbase(16) << &my_nullptr << endl;    //nullptr_t类型的对象取地址
    //cout << setbase(16) << &nullptr<< endl;  //nullptr是个右值常量,不能取地址。
    const nullptr_t&& def_nullptr = nullptr; //nullptr是个右值常量,可用右值引用来接。
    cout << "&def_nullptr = " << setbase(16) << &def_nullptr << endl; //可对右值引用取地址(具名变量,本身是左值)

    //2. nullptr与(void*)0的区别
    void* px = NULL;
    //int* py = (void*)0;  //编译错误,不能隐式将void*转为int*类型
    int* pz = (int*)px;    //void*不能隐式转为int*,必须强制转换!

    int* pi = nullptr;     //ok!nullptr可以隐式转为任何其他指针类型
    void* pv = nullptr;    //ok! nullptr可以隐式转为任何其他指针类型

    //3. 避免在整型和指针间重载函数
    f(0);        //invoke f(int)
    f(NULL);     //invoke f(int)
    f((char*)0); //invoke f(void*)
    f(nullptr);  //invoke f(void*)

    //4. nullptr的模板推导中,仍可当指针用。
    std::mutex m1, m2, m3;
    //auto result1 = lockAndCall(f1, m1, 0);    //0被推导为int类型,与share_ptr<Widget>类型不匹配
    //auto result2 = lockAndCall(f2, m2, NULL); //同上,NULL被优先当作整型
    auto result3 = lockAndCall(f3, m3, nullptr);//nullptr被推导为nullptr_t,但该类可以隐式转为Widget*

    return 0;
}
/*输出结果
&my_nullptr = 00DEFB28
&def_nullptr = 00DEFB10
invoke f(int)
invoke f(int)
invoke f(void*)
invoke f(void*)
*/