C++总结02、内联函数、auto关键字、范围for、nullptr

发布于:2023-01-26 ⋅ 阅读:(10) ⋅ 点赞:(0) ⋅ 评论:(0)

前言:本文章主要用于个人复习,追求简洁,感谢大家的参考、交流和搬运,后续可能会继续修改和完善。

因为是个人复习,会有部分压缩和省略。

一、内联函数

C语言为了避免小函数建立栈帧,提供了宏函数,会在预处理阶段展开

既然C语言已经解决了,为什么C++还要提供inline函数呢?(宏函数的缺点)

1.宏函数不支持调试

2.宏函数语法复杂,容易出错

3.宏函数没有类型安全的检查

C++推荐频繁调用的小函数,定义成inline,会在调用的地方展开,没有栈帧开销

能不能所有函数都搞成内联?

例如这里有一个sort函数有100行指令 ,有10个地方调用,总计是多少指令?

有110条。

如果搞成内联函数,有多少条?

100*10,有1000条指令。

指令变多意味着什么呢?

指令变多意味着编译出来的可执行程序变大,会使安装软件的人体验变差,执行程序内存消耗变多

结论:频繁调用的小函数,建议定义成inline

内联函数的特性:

1. inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。

2. inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体太大或有递归等等,编译器优化时会忽略掉内联。
3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

会发生链接错误的根本原因:内联函数没有地址!!! 

另外:直接在类里面定义实现的成员函数,默认就是内联函数

class A
{
    public:
        void func()
        {
            cout << "A" << endl;
        }
    private:
        int a;
};

二、auto

类型声明称auto,可以根据a的类型自动推导b的类型

(auto*)malloc()这样是不可以的

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{

    int a = 10;
    //int b = a;
    auto b = a;//类型声明成auto,可以根据a的类型自动推导b的类型
    map<string, string> m;
    //map<string, string>::iterator it = m.begin();
    //这里可以根据m.begin()自动推导it的类型是map<string, string>::iterator
    auto it = m.begin();

    return 0;
}

 auto有几个地方不能使用:

1.auto不能作为函数的参数

auto不能作为形参类型,因为编译器无法对a的实际类型进行推导

void test(auto a)
{}

2.auto不能直接用来声明数组

void test()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
    //那么可以这样吗?在创建数组时,auto b[0] = a[1];
        auto a = 4.1,b = 4;//也不可以
}

3.auto在实际中最常见的优势用法就是跟以后会讲道德C++11的新式for循环,还有lambda表达式等进行配合使用

auto真正的意义在于支持C++11的新语法范围for,新语法遍历,更简单,是数组都可以,容器也可以

e的名字可以改变

注意,这种情况下不能使用范围for

void test(int arr[])
{

    for(auto& e : arr)
    {
        cout << e << endl;
    }


}

范围for的使用条件
1. for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。注意:以下代码就有问题,因为for的范围不确定,int array[]表示的不是一个数组,它是一个指针,所以不行

void TestFor(int array[]) 
{
    for(auto& e : array) 
    cout<< e <<endl;
}

2. 迭代
的对象要实现++和==的操作。(关于迭代器这个问题,以后会讲)

三、nullptr和NULL

在C++中我们使用nullptr,因为C++中NULL其实是被define成0的,是这样的一个宏,不是指针

void f(int) 
{
    cout<<"f(int)"<<endl; 
}
void f(int*) 
{
    cout<<"f(int*)"<<endl; 
}
int main()
{
    f(0);
    f(NULL);
    f((int*)NULL); 
    return 0;
}

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。 
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下
将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)。