大厂面试重要C++知识(二)—— 内联函数和宏的区别与联系

news/2024/5/20 3:31:41 标签: c++, 编程语言, 内联函数, 面试

目录

内联函数和宏做为C和C++的基础,具有提高执行效率的功能。那么既然存在了宏,为什么还要内联函数?他们有什么区别和联系呢?以下展开。

一、内联函数

内联函数定义

  • 内联函数是C++为降低小型程序调用开销而采取的一种机制。
  • 函数在调用时,需要在栈中为形参和局部变量开辟空间,将实参的值赋值给形参;还有函数执行前的现场状态和返回地址都要压入栈中,以便函数返回后继续执行,这个过程带来时间和空间的开销。同理,在退出函数时也要时间的开销。
  • 因此,使用内联函数机制定义一些简单、代码短、常用到的函数。
  • 内联函数和普通函数的区别:编译器处理内联函数语句时,不会将语句编译成函数调用的指令,而是直接将整个函数体的代码嵌入调用语句处,就像整个函数体在调用处重写了一遍。
  • 个人理解,有点像编写代码时不写函数而直接书写相应的代码,只不过写内联函数是编译器替你做了这些事情(写重复的代码),而自己只需要封装好函数加上内联标志,编译器帮你完成重复代码的书写并翻译。

代码实现

在代码实现很简单,在定义函数时,在返回值类型上写inline关键字即可。

inline int Max(int a, int b)
{
    return a > b ? a : b; 
}

优缺点

优点:即可以方便我们像调用函数一样解决重复代码的问题,又不用付出函数调用时压栈、出栈的时间开销。

缺点:使用内联函数以空间换时间,增加了编译后的代码体积。如果函数里代码量大、出现了循环、递归等,执行函数的花销远大于调用函数的开销,那么就没有必要使用内联函数,甚至使得可执行程序变得很大,浪费大量的空间。

关键

  • 调用内联函数的语句前必须已经出现内联函数的定义(即完整的函数代码),而不能只出现内联函数的声明。

  • 在函数前头加上inline声明为内联函数,但是编译器会根据函数的大小、是否有循环等判断是否满足内联条件,不满足则不是内联函数

  • 内联一般在中使用,如get()、set()方法。

  • 内联的定义最好放在头文件,即整个函数体放在头文件里。这其实是个工程的问题,因为C++工程中的类的定义一般写在头文件中,具体实现代码在源文件中。一般来说,头文件中不允许出现函数的定义,但是由于内联函数必须在调用它的每个文本文件中被定义,所以没有在类体中定义的内联成员函数必须被放在类定义出现的头文件中。

    //这是test.h
    #pragma once
    class test
    {
    public:
    	int getValue();
    private:
    	int value;
    };
    inline int test::getValue()
    {
    	return value;
    }
    
    

二、内联函数和宏(macro)的区别

首先看看宏的定义:

  宏,也就是宏定义,是C语言三种预处理功能之一。例如:#define N 100,在预处理工作过程中,代码中出现的所有N(宏名),都会被100(替换文本)替代,并且是原地展开

  宏函数,也就是带参数的宏定义,和函数类似又不同。宏函数在预处理期间处理,而函数则在编译时处理;宏函数在上面提到是原地展开,直接将代码替换,并不需要像函数调用时的各种开销,速度更快。

不过宏函数有个缺点,宏是原地替换的,所以可能遇到运算符优先级的不同而带来错误。最好每个变量都加上括号。

比如下面这个错误案例:

#include <iostream>
using namespace std;

#define DOUBLE(x) 2 * x

int main()
{
    cout << DOUBLE(2) << endl;
    cout << DOUBLE(1 + 1) << endl;
}

  上面的代码,如果按照普通函数的思路,那么2 和 1 + 1其实是一致的,所以两者结果应该都是4,但是运行的结果却是4 和 3。原因是,宏在预处理时是原地替换,DOUBLE(1 + 1)在预处理时被替换为:2 * x + 1,自然结果就不是我们所想的。所以,宏函数中的参数都尽量带上括号,带上括号后在文本替换时就能够保证优先级顺序不出错。

  虽然上面的宏函数通过加括号后可以无误地完成需求,但是如果一个函数很复杂,每个变量都要加括号,写起来很费劲,代码看起来也很不舒服。所以,之前提到的内联函数就派上用场了。那么宏和内联函数有什么异同点呢?

内联函数和宏的区别

  • 宏是预处理时将代码原地替换(文本替换),而内联函数是在预处理的下一个步骤编译时将代码嵌入调用处。
  • 内联函数有类型检查、语法判断,而宏没有。
  • 宏和内联函数的展开还是有区别的,宏函数定义时每个参量都最好加上括号,而内联函数本质上还是函数并没有这么多的要求。
  • 内联函数通过inline关键字提醒编译器,编译器可以拒绝不符合条件的内联;而宏是在预处理时强制替换。
  • 宏不能直接访问类的私有成员,这种情况下使用内联函数会比较好,如get()、set()操作类的私有成员。
  • 因为宏在预处理时已经被原地替换了,所以宏函数不能够进入内部调试。
  • 两者相对于普通函数都有提高效率的作用,在不同场景使用根据需求使用宏或者内联,两者各有千秋。

参考文章


http://www.niftyadmin.cn/n/767767.html

相关文章

大厂面试重要C++知识(三)—— 智能指针

目录一、前言二、智能指针三、auto_ptrauto_ptr基本操作智能指针赋值四、unique_ptr五、share_ptr六、weak_ptr解决互相引用问题七、总结一、前言 C/C最让人诟病的特性之一&#xff1a;内存泄漏。因此如java、C#等语言都提供了内置内存分配与释放功能&#xff0c;屏蔽了指针。…

力扣每日一题:1190. 反转每对括号间的子串

目录题目&#xff1a;1190. 反转每对括号间的子串示例1示例2示例3示例4提示&#xff1a;解题思路解题代码&#xff08;1&#xff09;栈&#xff08;2&#xff09;栈——优化题目&#xff1a;1190. 反转每对括号间的子串 难度&#xff1a; 中等 题目&#xff1a; 给出一个字符…

详解C++STL容器系列(一)—— vector的详细用法和底层原理

目录一、介绍二、vector的创建和方法创建vector方法三、vector的具体用法3.1 遍历vector3.1.1 迭代器访问3.1.2 下标访问3.1.3 范围for循环3.2 vector 容量和大小3.3 vector 常用算法3.3.1 push_back、pop_back 和 emplace_back3.3.2 insert 和 emplace3.3.3 erase3.3.4 assign…

详解C++STL容器系列(二)—— list的详细用法和与vector的对比

目录一、list介绍二、list创建三、list方法对比vector四、list的具体用法4.1 iterators4.2 Capacity4.3 Element access4.4 Modifierspush_front、push_back、emplace_front、emplace_backinsert、emplaceeraseassignswap4.5 list operationsspliceremoveremove_ifuniquesortme…

力扣每日一题:474. 一和零

目录题目&#xff1a;474. 一和零示例1示例2提示&#xff1a;解题思路解题代码&#xff08;1&#xff09;动态规划&#xff08;2&#xff09;滚动数组&#xff0c;空间优化题目&#xff1a;474. 一和零 难度&#xff1a; 中等 题目&#xff1a; 给你一个二进制字符串数组 str…

力扣每日一题:494. 目标和

目录题目&#xff1a;494. 目标和示例1示例2提示&#xff1a;解题思路解题代码&#xff08;1&#xff09;回溯&#xff08;2&#xff09;动态规划&#xff08;01背包问题&#xff09;&#xff08;3&#xff09;背包问题——空间优化题目&#xff1a;494. 目标和 难度&#xff…

力扣每日一题:1049. 最后一块石头的重量 II

目录题目&#xff1a;1049. 最后一块石头的重量 II示例1示例2示例3提示&#xff1a;解题思路解题代码&#xff08;1&#xff09;动态规划&#xff08;2&#xff09;动态规划空间优化题目&#xff1a;1049. 最后一块石头的重量 II 难度&#xff1a; 中等 题目&#xff1a; 有一…

力扣每日一题:879. 盈利计划

目录题目&#xff1a;879. 盈利计划示例1示例2提示&#xff1a;解题思路解题代码&#xff08;1&#xff09;动态规划&#xff08;2&#xff09;动态规划空间优化题目&#xff1a;879. 盈利计划 难度&#xff1a; 困难 题目&#xff1a; 集团里有 n 名员工&#xff0c;他们可…