0
点赞
收藏
分享

微信扫一扫

初识C++ auto&nullptr

写在前面

这个应该是C++基础知识最后的一小部分,前面我们们说了这么多,都是为了之后的类和对象做铺垫,今天我们主要看看两个最基本的知识。

auto

这个关键字我们在C语言的时侯也专门和大家分享过,在C语言里我们都是忽略了这个关键字,不过在C++里面,这个关键字又被赋予的新的特性。

auto作用

在早期C/C++中auto的含义是:使用auto 修饰的变量,是具有自动存储器的局部变量,但遗憾的是一直没有
人去使用它.在C++11中,标准委员会赋予了auto全新的含义即:auto 不再是一个存储类型指示符,而是作为一个新的类型
指示符来指示编译器, auto 声明的变量必须由编译器在编译时期推导而得

说人话就是auto可以根据我们所给的数据自动推出他应该是什么类型.这一点很重要.

#include <iostream>
using namespace std;

int main()
{
    auto x = 1;
    auto ch = 'c';
    return 0;
}

image-20220514150850111

int TestAuto()
{
    return 10;
}
int main()
{
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = TestAuto();

    //typeid 可以测试 变量的类型
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl;
    return 0;
}

image-20220514151451125

好了,到这里auto的最基本的点已经说完了,大家可能会感到疑惑,我们分明可以定义数据的类型,为何还要auto,这不是多此一举吗?

为何要有auto

这个问题很好,如果是在C语言中,这个功能确实是鸡肋,但是在C++中,如果你学了模板,就会发现这个auto实在是太好用了,我先和大家演示一下,下面的代码看不懂不要紧,后面我们都会去认识的,这里就是为了举例子.

看看下面的代码.

int main()
{
    std::map<std::string, std::string> dict;
    dict["sort"] = "排序";
    dict["string"] = "字符串";

    std::map<std::string, std::string>::iterator it = dict.begin();

    return 0;
}

image-20220514152840767

变量的类型实在是太长了,但是我们是使用auto就不一样了,编译器会自动推道.

int main()
{
    std::map<std::string, std::string> dict;
    dict["sort"] = "排序";
    dict["string"] = "字符串";

    //std::map<std::string, std::string>::iterator it = dict.begin();
    auto it = dict.begin();
    return 0;
}

image-20220514153024887

auto的特性

我们已经知道了auto的基本用法了,现在可以看看它的一些特性了.

必须初始化

auto和引用一样,我们必须给auto修饰的变量初始化,否则无法编译.

int main()
{
    auto a;
    return 0;
}

image-20220514160554545

在同一行定义多个变量

auto可以在同一行定义多个变量,但是这些变量的类型必须是一样的否则就会出现报错.

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对
第一个类型进行推导,然后用推导出来的类型定义其他变量

int main()
{
    auto a = 1,b = 2;
    auto c = 1.0,b = 'c';  // 报错
    return 0;
}

image-20220514160809467

auto与指针和引用结合起来使用

我们来看看这两种的使用方法.

指针

这个我必须和大家要仔细的分析下,auto可以推导指针类型,这一点毋庸置疑.

int main()
{
    int a = 10;
    auto pa = &a;
    cout << typeid(pa).name() << endl;
    return 0;
}

image-20220514161307094

但是你看看下面的代码是不是正确的?

int main()
{
    int a = 10;
    auto* pa = &a;    //加了一个  *
    cout << typeid(pa).name() << endl;
    return 0;
}

image-20220514161437732

通过结果我们看出来,加不加* 的作用都是一样的,大家用的时候选择哪个都可以的.

引用

这里说一下,auto不能自动的推导出来该类型的引用,也就是说我们必须帮助编译器识别.

int main()
{
    int a = 10;
    auto& pa = a;    

    cout << "&a:  " << &a << endl;
    cout << "&pa: " << &pa << endl;
    return 0;
}

image-20220514162055242

auto不能推导的场景

auto也不是万能的,我们不能把所有的期望都寄托到编译器身上,auto有下面几种不能推导的地方

  • auto不能作为函数的参数
  • auto不能直接用来声明数组

基于范围的for循环(C++11)

我们在打印一个数组的时候,应该怎么办?我们来尝试一下.

int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10};
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < sz; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
    return 0;
}

image-20220514162946216

但是在C++11标准出来后,就可以使用范围for,这种方法很简单.

int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int val : arr)
    {
        cout << val << " ";
    }
    cout << endl;
    return 0;
}

image-20220514163248987

范围for的原理(粗讲)

现在我们还不能完全认识它的原理,只有到后面的迭代器才可以,现在我们就简单的谈谈它的原理.

编译器为 val 开辟一块空间,自动获取数组arr里面的元素,把他的值赋给val,直到编译器觉得数组已经打印完了.

for (auto val : arr)   //auto也可以在这里使用
{
    cout << *val << " ";
}

范围for的缺点

我们使用的范围for,也是存在一定的问题的,你会发现,他一打印就是打印整个数组,不会停下来.也就是说,编译器控制这整个打印流程.这就给我们加下来的打印造成了很大的困难.

范围for不能打印作为参数的数组,编译器找不到何时可以开始和停止的位置.

void func(int* arr)
{
    for (int val : arr)
    {
        cout << val << " ";
    }
    cout << endl;
}

int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    func();
    return 0;
}

image-20220514164012316

nullptr

nullptr 和我们==C语言==里面的NULL是一样的,都是空指针.但是C++里面的NULL和nullptr 可不一样.我们现在来看看C++里面的NULL

现在我们就要看看会调用哪个func函数.

void func(int* p)
{
    cout << "p是指针" << endl;
}

void func(int p)
{
    cout << "p是整型" << endl;
}

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

image-20220514165344048

我们蒙了,NULL不是空指针吗?为何会调用第二个函数?这不是有点荒唐吗?

NULL本质上是一个宏,我们看看C++关于NULL宏定义.

如果没有定义NULL,如果存在 __cplusplus这个宏(C++特有的),那就把数字0定义成NULL,否则就把地址0定义给NULL.也就是说C++里面的NULL就是数字0.nullptr 是一个关键字,它的原理是0地址.

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif
int main()
{
    func(nullptr);
    return 0;
}

image-20220514170109872

nullptr 特性

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr
举报

相关推荐

C++之auto

C++中NULL和nullptr的区别

C++ 初识

C++ auto类型推导

初识C++

0 条评论