C++心决之内联函数+auto关键字+指针空值

一枕眠秋雨>o< 2024-07-06 13:35:02 阅读 73

目录

7.内联函数

7.1 概念

7.2 特性

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

8.1 类型别名思考

8.2 auto简介

8.3 auto的使用细则

8.4 auto不能推导的场景

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

9.1 范围for的语法

9.2 范围for的使用条件

10. 指针空值nullptr(C++11)

10.1 C++98中的指针空值


7.内联函数

7.1 概念

inline

修饰

的函数叫做内联函数,

编译

C++

编译器会在

调用内联函数的地方展开

,没有函数调

用建立栈帧的开销,内联函数提升程序运行的效率。

如果在上述函数前增加

inline

关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的

调用。

查看方式:

1.

release

模式下,查看编译器生成的汇编代码中是否存在

call Add

2.

debug

模式下,需要对编译器进行设置,否则不会展开

(

因为

debug

模式下,编译器默认不

会对代码进行优化,以下给出VS2022

的设置方式

)

 

7.2 特性

1. inline

是一种

以空间换时间

的做法,如果编译器将函数当成内联函数处理,在

编译阶段,会

用函数体替换函数调用

,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运

行效率。

2.

inline

对于编译器而言只是一个建议,不同编译器关于

inline

实现机制可能不同

,一般建

议:将

函数规模较小

(

即函数不是很长,具体没有准确的说法,取决于编译器内部实现

)

是递归、且频繁调用

的函数采用

inline

修饰,否则编译器会忽略

inline

特性

3. inline

不建议声明和定义分离,分离会导致链接错误。因为

inline

被展开,就没有函数地址

了,链接就会找不到。

【面试题】

宏的优缺点?

优点:

1.

增强代码的复用性。

2.

提高性能。

缺点:

1.

不方便调试宏。(因为预编译阶段进行了替换)

2.

导致代码可读性差,可维护性差,容易误用。

3.

没有类型安全的检查 。

C++

有哪些技术替代宏

1.

常量定义 换用

const enum

2.

短小函数定义 换用内联函数

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

8.1 类型别名思考

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

1.

类型难于拼写

2.

含义不明确导致容易出错

在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的

类型。然而有时候要做到这点并非那么容易,因此

C++11

auto

赋予了新的含义。

8.2 auto简介

在早期

C/C++

auto

的含义是:使用

auto

修饰的变量,是具有自动存储器的局部变量

,但遗憾的

是一直没有人去使用它,大家可思考下为什么?

C++11

中,标准委员会赋予了

auto

全新的含义即:

auto

不再是一个存储类型指示符,而是作为一

个新的类型指示符来指示编译器,

auto

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

<code>int TestAuto()

{

return 10;

}

int main()

{

int a = 10;

auto b = a;

auto c = 'a';

auto d = TestAuto();

cout << typeid(b).name() << endl;

cout << typeid(c).name() << endl;

cout << typeid(d).name() << endl;

//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化

return 0;

}

使用

auto

定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导

auto

的实际类型

。因此

auto

并非是一种

类型

的声明,而是一个类型声明时的

占位符

,编译器在编

译期会将

auto

替换为变量实际的类型

8.3 auto的使用细则

1.

auto

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

auto

声明指针类型时,用

auto

auto*

没有任何区别,但用

auto

声明引用类型时则必须

&

int main()

{

   int x = 10;

   auto a = &x;

   auto* b = &x;

   auto& c = x;

   cout << typeid(a).name() << endl;

   cout << typeid(b).name() << endl;

   cout << typeid(c).name() << endl;

   *a = 20;

   *b = 30;

    c = 40;

   return 0;

}

2.

在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译

器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

8.4 auto不能推导的场景

1.

auto

不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导

void TestAuto(auto a)

{}

2.

auto

不能直接用来声明数组

void TestAuto()

{

   int a[] = {1,2,3};

   auto b[] = {4,5,6};

}

3.

为了避免与

C++98

中的

auto

发生混淆,

C++11

只保留了

auto

作为类型指示符的用法

4. auto

在实际中最常见的优势用法就是跟以后会讲到的

C++11

提供的新式

for

循环,还有

lambda

表达式等进行配合使用。

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

9.1 范围for的语法

C++98

中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()

{

int array[] = { 1, 2, 3, 4, 5 };

for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)

    array[i] *= 2;

for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)

    cout << *p << endl;

}

对于一个

有范围的集合

而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因

C++11

中引入了基于范围的

for

循环。

for

循环后的括号由冒号

分为两部分:第一部分是范

围内用于迭代的变量,第二部分则表示被迭代的范围

void TestFor()

{

int array[] = { 1, 2, 3, 4, 5 };

for(auto& e : array)

    e *= 2;

for(auto e : array)

    cout << e << " ";

return 0;

}

9.2 范围for的使用条件

1.

for

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

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

;对于类而言,应该提供

begin

end

的方法,

begin

end

就是

for

循环迭代的范围。

注意:以下代码就有问题,因为

for

的范围不确定

void TestFor(int array[])

{

   for(auto& e : array)

       cout<< e <<endl;

}

其实究其根本,如果传入数组名,范围for循环就可以拿到数组首元素地址,如果传入一个指针,拿到的就是指针本身的地址,而不是指针所指向空间的地址

 2. 迭代的对象要实现++==的操作

10. 指针空值nullptr(C++11)

10.1 C++98中的指针空值

在良好的

C/C++

编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现

不可预料的错误,比如未初始化的指针

NULL

实际是一个宏,在传统的

C

头文件

(stddef.h)

中,可以看到如下代码:

<code>#ifndef NULL

#ifdef __cplusplus

#define NULL   0

#else

#define NULL   ((void *)0)

#endif

#endif

可以看到,

NULL

可能被定义为字面常量

0

,或者被定义为无类型指针

(void*)

的常量

。不论采取何

种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

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

*)0

注意:

1.

在使用

nullptr

表示指针空值时,不需要包含头文件,因为

nullptr

C++11

作为新关键字引入

2.

C++11

中,

sizeof(nullptr)

sizeof((void*)0)

所占的字节数相同。

3.

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

nullptr



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。