C++ 中的内联函数

原文:https://www.studytonight.com/cpp/inline-functions.php

默认情况下,类定义中定义的所有成员函数都声明为 Inline。让我们对这些功能有一些背景知识。

你必须记住来自 C 语言的预处理器。C++ 中的内联函数和 C 语言中的宏做同样的事情。预处理器/宏没有在 C++ 中使用,因为它们有一些缺点。


C++ 中预处理程序/宏的缺点

在宏中,我们在程序开始时用它的值定义某个变量,在程序中我们使用那个变量的任何地方,它都被编译时的值所代替。

1)间距问题

让我们试着用一个例子来理解这个问题,

#define G (y) (y+1)

这里我们定义了一个名为G(y)的宏,在编译时用它的值来代替,也就是(y+1)。但是,当我们称之为G(y)时,实际上会发生什么,

G(1)  // Macro will replace it

预处理器会像这样展开它,

(y) (y+1) (1)

你一定在想为什么会这样,这是因为宏定义中的间距。因此,带有多个表达式的大函数永远不能与宏一起使用,所以在 C++ 中引入了 Inline 函数。

2)复杂论证问题

在某些情况下,这样的宏表达式对某些参数很有效,但是当我们使用复杂的参数时,问题就开始出现了。

#define MAX(x,y) x>y?1:0

如果我们用这个表达,

if(MAX(a&0x0f, 0x0f))  // Complex Argument

宏将扩展到,

if( a&0x0f > 0x0f ? 1:0)

这里运算符的优先级会导致问题,因为&的优先级比>低,所以宏观评估会让你大吃一惊。这个问题可以通过使用括号来解决,但是对于更大的表达式,问题还是会出现。

3)无法访问类的私有成员

使用宏,在 C++ 中,您永远无法访问私有变量,因此您必须将这些成员公开,这将暴露实现。

class Y
{
    int x;
    public : 
    #define VAL(Y::x)   // Its an Error
}

C++ 中的内联函数

内联函数是实际的函数,在编译过程中会被复制到各处,就像预处理器宏一样,这样就减少了函数调用的开销。默认情况下,类定义中定义的所有函数都是内联的,但是您也可以使用关键字将任何非类函数内联到它们上面。

对于内联函数,声明和定义必须一起完成。例如,

inline void fun(int a) 
{ 
    return a++; 
}

关于内联函数的几个要点

  1. 我们必须保持内联函数小,小的内联函数有更好的效率。
  2. 内联函数确实提高了效率,但是我们不应该让所有的函数都内联。因为如果我们内联大函数,可能会导致代码膨胀,也可能会影响速度。
  3. 因此,建议使用范围解析::运算符在类定义之外定义大型函数,因为如果我们在类定义之内定义这样的函数,那么它们会自动内联。
  4. 内联函数由编译器保存在符号表中,对这些函数的所有调用都在编译时进行。

C++ 中的 Getter 和 Setter 函数

我们已经在访问类内部的私有数据变量的主题中研究过这一点。我们使用内联的访问函数来实现这一点。

class Auto
{
    // by default private
    int price;

    public:
    // getter function for variable price
    int getPrice()
    {
        return price;
    }
    // setter function for variable price
    void setPrice(int x)
    {
        i=x;
    }
};

这里getPrice()setPrice()是内联函数,用于访问类Auto的私有数据成员。函数getPrice(),在这种情况下称为 Getter 或 Accessor 函数,函数setPrice()Setter 或 Mutator 函数。

也可以有重叠的存取函数和变异函数。我们将在下一个主题中研究重载函数


内联函数的局限性

  1. 大型内联函数会导致缓存未命中,并对性能产生负面影响。
  2. 编译时在代码中到处复制函数体的编译开销,对于小程序来说可以忽略不计,但是对于大代码库来说却有所不同。
  3. 另外,如果我们需要函数在程序中的地址,编译器就不能对这些函数进行内联。因为为了给函数提供地址,编译器必须给它分配存储空间。但是内联函数没有存储空间,它们保存在符号表中。

理解 C++ 中的前向引用

所有内联函数都由编译器在类声明的末尾进行评估。

class ForwardReference
{
    int i;
    public:
    // call to undeclared function
    int f() 
    {
        return g()+10;
    }
    int g() 
    {
        return i;
    }
};

int main()
{
    ForwardReference fr;
    fr.f();
}

您一定认为这将导致编译时错误,但在这种情况下,它会起作用,因为在类声明的右大括号之前,不会计算类中的内联函数。