从C语言到C++(第一章_C++入门_下篇)内联函数+auto关键字(C++11)+范围for +nullptr

这篇具有很好参考价值的文章主要介绍了从C语言到C++(第一章_C++入门_下篇)内联函数+auto关键字(C++11)+范围for +nullptr。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 内联函数

1.1 内联函数的概念

1.2 内联函数的特性

1.3 宏的优缺点和替代方法

2. auto关键字(C++11)

2.1 改版前的auto

2.2 C++11的auto

2.3 auto 的使用场景

2.4 使用auto的注意事项

3. 范围 for(C++11)

3.1 范围 for 的用法

3.2 范围 for 的使用条件

4. 指针空值 nullptr

4.1 C++ 98 中的指针空值

4.2 引入的nullptr

5. 笔试选择题

答案及解析

本篇完。(附下篇链接)


1. 内联函数

以前调用函数,需要建立栈帧,栈帧中要保留一些寄存器,结束后又要恢复。

这就可以看出这些都是有消耗的,对于频繁调用的小函数,有没有方法可以优化呢?

C语言可以用宏来优化,比如下面这个两数相加的函数,可以写一个宏代替

#include <iostream>
using namespace std;

int Add(int x, int y)
{
    int ret = x + y;
    return ret;
}

// 写一个两数相加的宏
#define ADD(X, Y) ((X) + (Y))//要注意()的使用

int main(void)
{
    cout << "函数: " << Add(1, 2) << endl;
    cout << "宏: " << ADD(1, 2) << endl;
    // 写宏的技巧:记住宏原理是替换,你替换一下看看对不对
    // cout << "M: " << ((1) + (2)) << endl;

    cout << "宏: " << 10 * ADD(3, 4) << endl;
    return 0;
}

宏有时候用起来似乎比较复杂,也容易出错。设计C++的大佬就弄出了内联函数来解决。


1.1 内联函数的概念

概念:以 inline 修饰的函数叫做内联函数。

编译时 C++ 编译器会在 调用内联函数的地方展开
没有函数调用建立栈帧的开销,内联函数可以提升程序运行的效率。

语法:inline 数据类型 [函数名]

(就是在以前写的函数前面+inline)

#include <iostream>
using namespace std;
 
inline int Add(int x, int y) 
{
    int ret = x + y;
    return ret;
}
 
int main()
{
    cout << "内联函数: " << Add(1, 2) << endl;
 
    return 0;
}

1.2 内联函数的特性

内联函数的特性:以空间换时间,省去了调用函数的开销。

编译时 C++ 编译器会在调用内联函数的地方展开,是没有函数压栈的开销的。

(内联内敛,内部关联)

因为内联函数会在编译的时候展开,所以代码很长。举个例子:

inline void func() 
{
    // 假设有10行代码
}

如果不展开,假设有1000个调用,编译后台就会有 10 + 1000 条指令。

如果展开,编译后台合计会有 10 * 1000 条指令,(所以内联函数适用于频繁调用的小函数

这是一场以空间换取时间的交易。因为没有了函数压栈的开销,所以能提高程序运行的效率。

注意事项:

① inline 既然是以空间换时间的做法,所以代码很长、循环或递归的函数不适宜成为内联函数

② inline 对于编译器而言只是一个建议,编译器会自动优化,如果定义为 inline 函数的函数体内有循环或递归(指令长)等等,编译器优化时会忽略掉内联。

③ inline 申明和定义不建议分离,分离会导致链接错误。内联函数会在调用的地方展开,导致不生成地址,链接(如平常写的Test.cpp)就会找不到。所以内联函数一般在.h或源文件直接实现。


1.3 宏的优缺点和替代方法

优点:

① 宏可以增强代码的复用性

② 宏有助于提高性能

缺点:

① 宏调试起来很不方便(因为宏在程序预编译阶段进行替换)。

② 宏的大量使用可能会导致代码的可读性差,可维护性差,容易误用。

③ 宏没有类型安全的检查。

C++有哪些技术替代宏?(C++中基本不再建议使用宏)

1. 常量定义 换用const enum

2. 短小函数定义 换用内联函数(内联函数几乎解决了宏的缺点和兼具了宏的优点)


2. auto关键字(C++11)

2.1 改版前的auto

改版前的 auto 指的是在早期 C/C++ 中 auto 关键字的含义。

旧的含义:使用 auto 修饰的变量,是具有自动存储器的局部变量。

遗憾的是,大家都懒得去用它。这是为什么呢?

auto int a = 0;   // 表示a是一个自动存储类型,会在函数结束后自动销毁。

当使用 auto 修饰后,表示 是一个自动存储类型,它会在函数结束以后自动销毁。

但是因为后来C语言把标准给改了,不加也是自动销毁:这么一来,这个 auto 关键字就没有意义了,因为都是自动销毁。


2.2 C++11的auto

为了缓解 auto 的尴尬,C++ 标准委员会把 auto 原来的功能给废弃了。

并赋予了 auto 全新的含义:

auto 现在不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器。

auto 声明的变量必须由编译器在编译时推导而得。

也就是说,它可以自动推导出数据的类型:

int a = 0;
auto c = a;  // C++11给auto关键字赋予了新的意义:自动推导c的类型

右边是什么,它就会推导出相应的类型,任何类型都可以实现,包括但不限于:

auto ch = 'A';
auto e = 10.11;
auto pa = &a;

为了方便测试,我们来打印一下对象的类型看看:

#include<iostream>
using namespace std;
int main()
{
    int a = 0;
    auto c = a;  // 自动推导c的类型
    auto ch = 'A';
    auto e = 10.01;
    auto pa = &a;
    // typeid - 打印对象的类型
    cout << typeid(a).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(ch).name() << endl;
    cout << typeid(e).name() << endl;
    cout << typeid(pa).name() << endl;
    return 0;
}

从C语言到C++(第一章_C++入门_下篇)内联函数+auto关键字(C++11)+范围for +nullptr

这时候可能有人会觉得,这一波操作好像也没啥意义啊,直接写数据类型不方便吗?  


2.3 auto 的使用场景

处理很长的数据类型:

在后面学完STL遇到这种场景,就能体会到 auto 的方便了:

#include <iostream>
#include <map>
 
int main() 
{
    std::map<std::string, std::string> dict = {{"sort", "排序"}, {"insert", "插入"}};
    std::map<std::string, std::string>::iterator it = dict.begin();
    // 这个类型又臭又长,写起来太麻烦了

    // 可以改成这样就方便多了   
    auto it = dict.begin();   // 根据右边的返回值去自动推导it的类型
 
    return 0;
}

auto 与指针结合起来使用:

auto 非常聪明,它在推导的时候其实是非常灵活的:

int main()
{
    int x = 10;
    auto a = &x;  // int*
    auto* b = &x; // int*
    auto& c = x;  // int
    return 0;
}

当在同一行声明多个变量时,这些变量必须是相同的类型。

否则编译器将会报错,因为编译器实际只对第一个类型进行推导,

然后用推导出来的类型定义其他变量。

auto a = 1, b = 2;
auto c = 3, d = 4.0; //该行代码会编译失败,因为c和d的初始化表达式类型不同

2.4 使用auto的注意事项

①使用 auto 是必须要给值的

int i = 0;
auto j; // 报错
 
auto j = i; // 必须给值
使用 auto 定义变量时必须对其进行初始化,
在编译阶段编译器需要根据初始化表达式来推导 auto
的实际类型。因此 auto 并非是一种 类型 的声明,而是一个类型声明时的 占位符
编译器在编 译期会将 auto 替换为变量实际的类型

②auto 不能作为函数的参数 auto 不能作为形参类型,因为编译器无法对a的类型进行推导。

③auto 不能直接用来声明数组

④用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须 加&。

为了避免与 C++98 中的 auto 发生混淆,C++11 只保留了 auto 作为类型指示符的用法。

auto 在实际中最常见的优势用法就是 C++11 提供的新式 for 循环,

还有 lambda 表达式等进行配合使用。


3. 范围 for(C++11)

在以前的C语言和 C++98 中如果要遍历一个数组,可以按照以下方式进行:
int main()
{
    int arr[] = { 1, 2,3,4,5 };
    int sz = sizeof(arr) / sizeof(arr[0]);  // 计算数组大小
    int i = 0;
    for (i = 0; i < sz; i++) 
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
 
    return 0;
}

对于一个有范围的集合而言,让程序员来说明循环的范围是多余的,有时候还会容易犯错误。

因此,C++11中引入了基于范围的 for 循环。


3.1 范围 for 的用法

语法: for ( 类型 变量名 : 数组)

for 循环后的括号由冒号分为两部分:

第一部分:范围内用于迭代的变量

第二部分:表示被迭代的范围

常这样用:

#include<iostream>
using namespace std;
int main()
{
    int arr[] = { 1, 2, 3, 4, 5 };
    for (auto& e : arr)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
}

是不是很方便,范围for就相当于一个语法糖(方便(甜))

试着使用范围 for,把数组中的每个值 +1 (1 2 3 4 5 → 2 3 4 5 6)

#include<iostream>
using namespace std;
int main()
{
    int arr[] = { 1, 2, 3, 4, 5 };
    for (auto& e : arr)//注意这里要用引用,不能用指针
    {
        e++;
    }

    for (auto e : arr)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
}

        注意事项:范围for和普通循环类似,可以用 continue 来结束本次循环,也可以用 break 来跳出整个循环。


3.2 范围 for 的使用条件

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

对于数组而言,就是数组中第一个元素和最后一个元素的范围;

对于后面学的类而言,应该提供 begin 和 end 的方法,begin 和 end 就是 for 循环迭代的范围。

错误演示:下面的代码就是 for 循环范围不确定。 

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

这里传递过来的是数组的首元素地址,并不是数组,它会不知道范围是多少,所以会报错。


4. 指针空值 nullptr

4.1 C++ 98 中的指针空值

在良好的 C/C++ 变成习惯中,声明一个变量时最好给该变量一个合适的初始值,

否则可能会出现不可预料的错误,比如未初始化的指针。

如果一个指针没有合法的指向,我们就需要手动给它置为空。

在之前学的C语言里,我们都是用 NULL 来解决的:

#include<iostream>
using namespace std;
 
int main()
{
    // C++ 98/03
    int* p = NULL;//其实相当于int* p = 0;
    return 0;
}
NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

4.2 引入的nullptr

正如之前所说,NULL 其实是一个宏:

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针 (void*) 的常量。

不论采取何种定义,在使用空值和指针时,都不可避免地会遇到一些麻烦,比如:

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

该程序的本意是想通过 Func(NULL) 调用指针版本的 Func(int*) 函数,

但是由于 NULL 被定义成0,这么一来就不符合程序的初衷了。

在 C++98 中,字面常量 0 既可以是一个整型数字,也可以是无类型的指针 (void*) 常量,

但是编译器默认情况下会将其看成一个整型常量,如果要将其按照指针方式来使用,

必须对其进行强制类型转换 (void*)0

后来C++11引入了指针空值 nullptr 就缓解了这一尴尬现象。

C++ 空指针推荐使用 nullptr 来处理

int* p = nullptr;

这是 C++11 新增的关键字,以后就不再推荐使用 NULL 了。

注意事项

① 使用 nullptr 表示指针空值时,因为它是关键字,所以不需要包含头文件。

C++11 中,sizeof( nullptr ) 与 sizeof( (void*)0 ) 所占的字节数相同。

③ 为了提高代码的健壮性,在后续表示指针空值的时建议最好使用 nullptr 。

总结就是NULL 是数字0  nullptr是(void*)0


5. 笔试选择题

妹说就是单选(不定项选择和多选是不一样的)

5.1 下面关于C++命名空间描述错误的是( )【不定项选择】

A.命名空间定义了一个新的作用域。

B.std是C++标准库的命名空间。

C.在C++程序中,命名空间必须写成using namespace std;

D.我们可以自己定义命名空间。


5.2 下面关于C++缺省参数描述错误的是( ) 【不定项选择】

A.缺省参数是声明或定义函数时为函数的参数指定一个默认值.

B.在调用有缺省参数的函数时,如果没有指定实参则采用该默认值,否则使用指定的实参

C.C和C++都支持缺省参数

D.全缺省就是参数全部给缺省值,半缺省就是缺省一半的值


5.3 关于引用以下说法错误的是( )。(阿里巴巴2015笔试题)

A.引用必须初始化,指针不必

B.引用初始化以后不能被改变,指针可以改变所指的对象

C.不存在指向空值的引用,但是存在指向空值的指针

D.一个引用可以看作是某个变量的一个“别名”

E.引用传值,指针传地址

F.函数参数可以声明为引用或指针类型


5.4 以下不是double compare(int,int)的重载函数的是( )

A.int compare(double,double)

B.double compare(double,double)

C.double compare(double,int)

D.int compare(int,int)


5.5 下面关于函数重载描述错误的是( )【多选】

A.构成函数重载的条件是函数名相同就可以

B.重载的函数参数可以相同

C.重载的函数返回值可以相同

D.C和C++都支持函数重载


5.6 “引用”与指针的区别是什么( )

A.指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作

B.引用通过某个引用变量指向一个对象后,对它所指向的变量间接操作。程序中使用引用,程序的可读性差;而指针本身就是目标变量的别名,对指针的操作就是对目标变量的操作

C.指针比引用更节省存储空间

D.以上都不正确


5.7 关于引用与指针的区别,下面叙述错误的是( )

A.引用必须被初始化,指针不必

B.指针初始化以后不能被改变,引用可以改变所指的对象

C.删除空指针是无害的,不能删除引用

D.不存在指向空值的引用,但是存在指向空值的指针


5.8 在( )情况下适宜采用 inline 定义内联函数

A.函数体含有循环语句

B.函数体含有递归语句

C.函数代码少、频繁调用

D.函数代码多,不常调用


5.9 关于c++的inline关键字,以下说法正确的是( )

A.使用inline关键字的函数会被编译器在调用处展开

B.头文件中可以包含inline函数的声明

C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数

D.递归函数也都可以成为inline函数


答案及解析

5.1  C     选错误的emmmm谁掉坑了我不说

A.命名空间主要解决名字冲突,其作用就是定义一个新的作用域

B.std是标准命名空间

C.还有std::xx 和using std::xx来使用标准库中的用法,所以不是必须写using namespace std

D.可以通过namespace space_name{};定义自己新的命名空间

5.2 C

A.缺省参数就是给出的函数参数的默认值

B.很明显,这是给出缺省值的意义所在

C.纯C语言,即.c文件,函数不支持缺省参数,C++即.cpp文件支持

D.半缺省不能随便缺省一半,必须从右往左缺省,否则编译出错

5.3 E

A.引用必须初始化,必须在定义引用时明确引用的是哪个变量或者对象,否则语法错误,指针不初  始化时值为随机指向

B.引用一旦定义时初始化指定,就不能再修改,指针可以改变指向

C.引用必须出示化,不能出现空引用,指针可以赋值为空

D.简单粗暴的引用理解可以理解为被引用变量或对象的"别名"

E.引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做,所以错误

F.函数调用为了提高效率,常使用引用或指针作为函数参数传递变量或对象

5.4 D

A.重载必须是参数列表有所不同(包括个数和类型),所以参数类型不同,构成重载

B.参数类型不同,构成重载

C.参数类型不同,构成重载

D.函数重载不能依靠返回值的不同来构成重载,因为调用时无法根据参数列表确定调用哪个重载函数,故错误

5.5 ABD

A.函数名相同只是函数重载的条件之一

B.重载函数必须参数列表有所不同(包括参数类型和参数个数)

C.重载函数不依靠返回值来区分,所以返回值可以相同

D.C不支持函数重载

5.6 A

A.指针是间接操作对象,引用时对象的别名,对别名的操作就是对真实对象的直接操作,故正确

B.很显然,答案刚好相反

C.指针需要开辟空间,引用不需要开辟空间,故错误

D.emmmm

5.7 B

A.引用必须定义时初始化,指针不初始化其值为随机指向

B.指针可以改变指向,引用不能,故错误

C.空指针没有任何指向,删除无害,引用是别名,删除引用就删除真实对象

D.引用必须初始化,所以不能为空引用,指针可以

5.8 C

A.含有循环语句,违背内联函数的本质

B.含有递归语句,违背内联函数的本质

C.尽可能把代码短小,频繁调用的函数设置为内联函数

D.含代码量大,违背内联函数的本质

内联函数是一种建议,如果函数内部包括循环,递归,或者  代码量大且复杂,这些函数即使设置了内联函数,系统也不会当做内联函数来处理。

5.9 C

A.不一定,因为inline只是一种建议,需要看此函数是否能够成为内联函数

B. inline函数不支持声明和定义分离开,因为编译器一旦将一个函数作为内联函数处理,就会在调用位置展开,即该函数是没有地址的,也不能在其他源文件中调用,故一般都是直接在源文件中定义内联函数的

C.inline函数会在调用的地方展开,所以符号表中不会有inline函数的符号名,不存在链接冲突(如果在同一个项目不同的.c文件定义同名的普通函数就会发生链接错误)

D.比较长的函数,递归函数就算定义为inline,也会被编译器忽略,故错误


本篇完。(附下篇链接)

下一篇:类和对象。

(穿越回来复习顺便放个下篇链接:从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针_GR C的博客-CSDN博客)文章来源地址https://www.toymoban.com/news/detail-419943.html

到了这里,关于从C语言到C++(第一章_C++入门_下篇)内联函数+auto关键字(C++11)+范围for +nullptr的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】引用、内联函数、auto关键字等

    前言:在前面我们讲解了C++入门基础的一些学习例如命名空间、缺省参数、函数重载等。今天我们将进一步的学习,跟着博主的脚步再次往前迈一步吧。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:高质量C++学习 👈 💯代码仓库:卫卫周大胖的学习日记💫 💪关注博

    2024年02月20日
    浏览(62)
  • 【C++初阶(三)】引用&内联函数&auto关键字

    目录  前言  1. 引用   1.1 引用的概念   1.2 引用的特性  1.3 引用的权限  1.4 引用的使用  1.5 引用与指针的区别 2. 内联函数 2.1  什么是内联函数 2.2  内联函数的特性  3. auto  3.1 auto简介  3.2 auto使用规则  3.3 auto不能使用的场景 4.  基于范围的for循环  4.1 范围for使用

    2024年02月08日
    浏览(59)
  • 【C++杂货铺】内联函数、auto、范围for、nullptr

     普通的函数在调用的时候会开辟函数栈帧,会产生一定量的消耗,在C语言中可以用 宏函数 来解决这个问题,但是宏存在以下缺陷: 复杂、容易出错、可读性差、不能调试 。为此,C++中引入了 内联函数 这种方法。  以 inline 修饰 的函数叫做内联函数, 编译时 C++编译器会

    2024年02月16日
    浏览(55)
  • 【C++那些事儿】内联函数,auto,以及C++中的空指针nullptr

    君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,我之前看过一套书叫做《明朝那些事儿》,把本来枯燥的历史讲的生动有趣。而C++作为一门接近底层的语言,无疑是抽象且难度颇深的。我希望能努力把抽象繁多的知识讲的生

    2024年02月08日
    浏览(42)
  • [C++] C++入门第二篇 -- 引用& -- 内联函数inline -- auto+for

      目录 1、引用 -- 1.1 引用的概念 1.2 引用特性 1.3 常引用 -- 权限问题 1.4 引用的使用场景 1.4.1 做参数 1.4.2 做返回值 注意 1.5 传值、传引用的效率比较 1.6 引用和指针的区别 2、内联函数 2.1 概念 转存失败重新上传取消​编辑转存失败重新上传取消​编辑2.2 特性 3、auto 3.1 auto简

    2024年02月15日
    浏览(48)
  • 【C++】C++入门第二课(函数重载 | 引用 | 内联函数 | auto关键字 | 指针空值nullptr)

    目录 前言 函数重载 概念 重载函数的条件 C++支持重载函数的原理--名字修饰 引用 概念 特性 常引用(const引用) 使用场景 传值,传引用效率比较 引用和指针的区别 内联函数 概念 特性 auto(C++11) auto简介 auto的使用规则 指针空值nullptr(C++11) C++98中的指针空值 结语

    2024年04月15日
    浏览(48)
  • 【C++初阶】引用&内联函数&auto关键字&范围for循环&nullptr

    ========================================================================= 个人主页还有更多系列专栏: 小白不是程序媛 我的小仓库: Gitee C++系列专栏: C++头疼记 ========================================================================= 目录 前言 引用 概念 引用的特点 常引用  引用的使用场景 做参数  做返

    2024年02月08日
    浏览(46)
  • 【C++心愿便利店】No.3---内联函数、auto、范围for、nullptr

    👧个人主页:@小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C++ 心愿便利店 🔑本章内容:内联函数、auto、范围for、nullptr 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:以下是本篇文章正文内容,下面案例可供参考 通过对C语言的学习,对于宏有了一定

    2024年02月11日
    浏览(42)
  • 开启C++之旅(下):引用、内联函数及现代特性(auto和范围for循环)

    上次介绍了:开启C++之旅(上):探索命名空间与函数特性(缺省参数和函数重载) 今天就接着进行c++入门的知识讲解 引用 不是新定义一个变量,而是给已存在 变量取了一个别名 ,编译器不会为引用变量开辟内存空间,它和它引用的变量 共用 同一块内存空间。通过引用,

    2024年01月17日
    浏览(78)
  • 【C++】:函数重载,引用,内联函数,auto关键字,基于范围的for循环,nullptr关键字

    在C语言中,同名函数是不能出现在同一作用域的,但是在C++中却可以,但是要满足函数重载的规则。 那什么是函数重载呢?它的规则是什么呢? 函数重载 :是函数的一种特殊情况,C++允许在 同一作用域 中声明几个功能类似的 同名函数 ,这些同名函数的 形参列表(参数个数

    2024年04月26日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包