C语言向C++过渡的基础知识(三)

目录

auto类型变量(C++11标准支持)

auto关键字介绍

auto关键字的使用

auto关键字基本使用

auto关键字配合指针和引用

auto关键字不可以推导的场景

基于范围的for循环(C++11标准支持)

基于范围的for循环基础使用

基于范围的for循环使用条件

空指针值(C++11标准支持)


声明:本篇时C语言向C++过渡的基础知识的最后一篇

auto类型变量(C++11标准支持)

auto关键字介绍

随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:

  1. 类型难于拼写
  2. 含义不明确导致容易出错

例如下面的代码中

#include <string>
#include <map>
int main()
{
    std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange", 
"橙子" }, 
                                       {"pear","梨"} };
    std::map<std::string, std::string>::iterator it = m.begin();
    while (it != m.end())
    {
        //....
    }
    return 0;
}

可以用typedef来处理上面的问题,但是typedef也有别的问题

#include <string>
#include <map>
typedef std::map<std::string, std::string> Map;
int main()
{
    Map m{ { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };
    Map::iterator it = m.begin();
    while (it != m.end())
    {
        //....
    }
    return 0;
}

考虑下面的问题

typedef char* pstring;
int main()
{
    const pstring p1;    // 编译失败,展开后为char* const p1,作为const修饰的变量为常量,只有一次赋值的机会,故需要初始化
    const pstring* p2;   // 编译通过
    return 0;
}

auto关键字的使用

auto关键字基本使用

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量

在C++11标准中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

使用auto关键字创建变量的语法如下:

auto 变量名;

auto创建的变量有自动推导类型的特点,例如下面的代码中

//auto关键字
#include <iostream>
using namespace std;

int main()
{
    int a = 0;
    auto b = a;//b自动推导为int类型
    double c = 0.0;
    auto d = c;//c自动推导为double类型

    cout << "a变量的类型为:" << typeid(a).name() << endl;
    cout << "b变量的类型为:" << typeid(b).name() << endl;
    cout << "c变量的类型为:" << typeid(c).name() << endl;
    cout << "d变量的类型为:" << typeid(d).name() << endl;

    return 0;
}
输出结果:
a变量的类型为:int
b变量的类型为:int
c变量的类型为:double
d变量的类型为:double

在上面的代码中,创建了两个普通对象ac,使用auto关键字创建了两个对象bd,分别自动匹配了ac的类型为intdouble,使用typeid.name()查看变量类型

需要注意的是,使用auto创建的变量一定要初始化,否则编译器无法推导该变量的类型从而报错

//auto关键字
#include <iostream>
using namespace std;

int main()
{
    auto e;// 报错

    return 0;
}
报错信息:
“e”: 类型包含“auto”的符号必须具有初始值设定项

所以,auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型

使用auto关键字可以同时创建多个变量,但是必须保证一个auto关键字创建的多个变量匹配一种类型,不可以auto匹配多个类型

#include <iostream>
using namespace std;

int main()
{
    auto a = 1, b = 2, c = 3, d = 4;//所有变量匹配为int类型
    auto f = 2.0, g = 2;//不可以同时匹配多个类型

    return 0;
}
报错信息:
在声明符列表中,“auto”必须始终推导为同一类型

auto关键字配合指针和引用

auto关键字创建的变量也可以自动推导为指针和引用类型,也可以直接将auto关键字创建的变量指定为指针和引用类型,用auto声明指针类型时,用autoauto*没有任何区别,但用auto声明引用类型时则必须加&

如下

//auto关键字配合指针和引用
#include <iostream>
using namespace std;

int main()
{
    int a = 0;
    int* p = &a;
    auto p1 = p;//自动推导为int*类型
    auto* p2 = p;//指定为int*类型
    //auto* p3 = a;//不可以推导
    auto r1 = a;//自动推导为int类型
    auto& r2 = a;//指定为引用类型

    return 0;
}

编译器指示如下:

auto关键字不可以推导的场景

  • auto关键字创建的变量不可以作为函数形参
void test(auto a)
{

}

#include <iostream>
using namespace std;

int main()
{
    test(1);
    return 0;
}
报错信息:
参数不能为包含“auto”的类型
  • auto不能直接用于声明数组
#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };
    auto arr1[] = { 1,2,3,4,5 };//不可以直接用来创建数组

    return 0;
}
报错信息:
“auto”类型不能出现在顶级数组类型中

但是可以获取到数组的地址

#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };
    auto arr1 = arr;//可以将已经创建的数组给auto创建的变量,自动匹配为int*
    cout << "arr1:" << typeid(arr1).name() << endl;
    for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
    {
        cout << "arr1[i]:" << arr1[i] << endl;
    }

    return 0;
}
输出结果:
arr1:int * __ptr64
arr1[i]:1
arr1[i]:2
arr1[i]:3
arr1[i]:4
arr1[i]:5

基于范围的for循环(C++11标准支持)

基于范围的for循环基础使用

在C++98中需要按照常规方式进行数组的遍历,例如

#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };
    for (int i = 0; i < sizeof(arr)/sizeof(int); i++)
    {
        cout << arr[i] << " ";
    }

    return 0;
}
输出结果:
1 2 3 4 5

但是在C++11中可以按照下面的方式进行遍历

#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };

    for (int num : arr)
    {
        cout << num << " ";
    }

    return 0;
}

在上面的for循环中,创建了一个num变量,将数组中的元素给num变量,再打印num变量

鉴于上面的过程说明,如果想用上面的方式修改数组中的元素则不会成功

#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };

    for (int num : arr)
    {
        num *= 2;
        cout << num << " ";
    }
    cout << endl;
    for (int num : arr)
    {
        cout << num << " ";
    }

    return 0;
}
输出结果:
2 4 6 8 10
1 2 3 4 5

此时可以配合引用进行原数组内容的修改

#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };

    for (int& num : arr)
    {
        num *= 2;
        cout << num << " ";
    }
    cout << endl;
    for (int num : arr)
    {
        cout << num << " ";
    }

    return 0;
}
输出结果:
2 4 6 8 10
2 4 6 8 10

📌

与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环

基于范围的for循环也可以使用auto创建循环变量

#include <iostream>
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5 };

    for (auto num : arr)
    {
        cout << num << " ";
    }
    cout << endl;
    for (auto& num : arr)
    {
        num *= 2;
        cout << num << " ";
    }

    return 0;
}
输出结果:
1 2 3 4 5
2 4 6 8 10

基于范围的for循环使用条件

for循环迭代的范围必须是确定的

#include <iostream>
using namespace std;
void test(int* arr)
{
    for (auto num : arr)
    {
        cout << num << " ";//不可以遍历,因为无法确定范围,数组在传参时只会传递第一个元素的地址
    }
}

int main()
{
    int arr[] = { 1,2,3,4,5 };
    test(arr);
    return 0;
}

空指针值(C++11标准支持)

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,基本都是按照如下方式对其进行初始化:

void TestPtr()
{
 int* p1 = NULL;
 int* p2 = 0;
 
 // ……
}

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

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

在上面的宏定义中,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量,不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如在函数重载中可能会使编译器匹配参数为整型的函数,而不是参数为指针类型的函数:

#include <iostream>
using namespace std;

void test(int a)
{
    cout << "匹配int类型形参" << endl;
}

void test(int* a)
{
    cout << "匹配int*类型形参" << endl;
}

int main()
{
    test(0);
    test(NULL);
    return 0;
}
输出结果:
匹配int类型形参
匹配int类型形参

此时为了使形参匹配到指针类型,必须将NULL或者0强制转换为(void*)类型

void test(int a)
{
    cout << "匹配int类型形参" << endl;
}

void test(void* a)
{
    cout << "匹配int*类型形参" << endl;
}

int main()
{
    test(0);
    test((void*)NULL);
    return 0;
}

为了解决上面的问题,C++11标准中提出了nullptr用于指定指针空值

#include <iostream>
using namespace std;

void test(int a)
{
    cout << "匹配int类型形参" << endl;
}

void test(int* a)
{
    cout << "匹配int*类型形参" << endl;
}

int main()
{
    test(0);
    //test((void*)NULL);
    test(nullptr);
    return 0;
}
输出结果:
匹配int类型形参
匹配int*类型形参

在使用nullptr时注意下面的三点:

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

int main()
{
    cout << "sizeof(NULL):" << sizeof(NULL) << endl;
    cout << "sizeof(nullptr):" << sizeof(nullptr) << endl;
    cout << "sizeof((void*)0):" << sizeof((void*)0) << endl;
    return 0;
}
输出结果:
sizeof(NULL):4
sizeof(nullptr):8
sizeof((void*)0):8
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怡晗★

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值