我什么时候应该为函数/方法编写关键字“内联”?

新手上路,请多包涵

我什么时候应该为 C++ 中的函数/方法编写关键字 inline

看到一些答案后,一些相关的问题:

  • 什么时候 应该为 C++ 中的函数/方法编写关键字“内联”?

  • 编译器何时不知道何时将函数/方法“内联”?

  • 当为函数/方法编写“内联”时,应用程序是否是 多线程 的是否重要?

原文由 Partial 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 605
2 个回答

哦,伙计,我最讨厌的事情之一。

inline 更像是 staticextern 而不是告诉编译器内联函数的指令。 extern , static , inline 是链接指令,几乎只由链接器使用,而不是编译器。

据说 inline 暗示编译器你认为函数应该被内联。这在 1998 年可能是正确的,但十年后编译器不需要这样的提示。更不用说在优化代码时人类通常是错误的,所以大多数编译器完全忽略了“提示”。

  • static - 变量/函数名不能在其他翻译单元中使用。链接器需要确保它不会意外使用来自另一个翻译单元的静态定义的变量/函数。

  • extern - 在此翻译单元中使用此变量/函数名称,但如果未定义,请不要抱怨。链接器将对其进行排序,并确保所有尝试使用外部符号的代码都有其地址。

  • inline 这个函数会在多个翻译单元中定义,不用担心。链接器需要确保所有翻译单元都使用变量/函数的单个实例。

注意: 通常,声明模板 inline 是没有意义的,因为它们已经具有 inline 的链接语义。但是,模板的显式特化和实例化 需要使用 inline


您的问题的具体答案:

  • > 我什么时候应该为 C++ 中的函数/方法编写关键字“内联”?

仅当您希望在标题中定义函数时。更准确地说,只有当函数的定义可以出现在多个翻译单元中时。在头文件中定义小(如在一个衬里)函数是一个好主意,因为它为编译器提供了更多信息以在优化代码时使用。它还增加了编译时间。

  • > 什么时候不应该为 C++ 中的函数/方法编写关键字“内联”?

不要仅仅因为您认为如果编译器内联您的代码会运行得更快,就添加内联。

  • > 编译器何时不知道何时将函数/方法“内联”?

一般来说,编译器会比你做得更好。但是,如果没有函数定义,编译器就没有内联代码的选项。在最大限度优化的代码中,无论您是否要求,通常所有 private 方法都是内联的。

为了防止在 GCC 中进行内联,请使用 __attribute__(( noinline )) ,在 Visual Studio 中,使用 __declspec(noinline)

  • > 当为函数/方法编写“内联”时,应用程序是否是多线程的是否重要?

多线程不会以任何方式影响内联。

原文由 deft_code 发布,翻译遵循 CC BY-SA 4.0 许可协议

一个用例可能发生在继承上。例如,如果以下所有情况都为真:

  • 你有某个类的基类
  • 基类必须是抽象的
  • 基类除了析构函数外没有纯虚方法
  • 您不想为基类创建 cpp 文件,因为徒劳

然后你必须定义析构函数;否则,您将遇到一些 undefined referance 链接器错误。此外,您不仅要定义,还要使用 inline 关键字定义析构函数;否则,您将出现 multiple definition 链接器错误。

这可能发生在一些只包含静态方法或编写基础异常类等的辅助类上。

我们举个例子:

基数.h:

 class Base {
public:
    Base(SomeElementType someElement) noexcept : _someElement(std::move(someElement)) {}

    virtual ~Base() = 0;

protected:
    SomeElementType _someElement;
}

inline Base::~Base() = default;

派生1.h:

 #include "Base.h"

class Derived1 : public Base {
public:
    Derived1(SomeElementType someElement) noexcept : Base(std::move(someElement)) {}

    void DoSomething1() const;
}

派生1.cpp:

 #include "Derived1.h"

void Derived1::DoSomething1() const {
    // use _someElement
}

派生2.h:

 #include "Base.h"

class Derived2 : public Base {
public:
    Derived2(SomeElementType someElement) noexcept : Base(std::move(someElement)) {}

    void DoSomething2() const;
}

派生2.cpp:

 #include "Derived2.h"

void Derived2::DoSomething2() const {
    // use _someElement
}

通常,抽象类除了构造函数或析构函数之外还有一些纯虚方法。因此,您不必将基类的虚拟析构函数的声明和定义分开,您可以在类声明上写 virtual ~Base() = default; 。但是,在我们的案例中,情况并非如此。

据我所知,MSVC 允许您在类声明中编写类似的内容: virtual ~Base() = 0 {} 。因此,您不需要使用 inline 关键字将 decleration 和定义分开。但它只适用于 MSVC 编译器。

现实世界的例子:

BaseException.h:

 #pragma once

#include <string>

class BaseException : public std::exception {
public:
    BaseException(std::string message) noexcept : message(std::move(message)) {}
    virtual char const* what() const noexcept { return message.c_str(); }

    virtual ~BaseException() = 0;

private:
    std::string message;
};

inline BaseException::~BaseException() = default;

SomeException.h:

 #pragma once

#include "BaseException.h"

class SomeException : public BaseException {
public:
    SomeException(std::string message) noexcept : BaseException(std::move(message)) {}
};

SomeOtherException.h:

 #pragma once

#include "BaseException.h"

class SomeOtherException : public BaseException {
public:
    SomeOtherException(std::string message) noexcept : BaseException(std::move(message)) {}
};

主.cpp:

 #include <SomeException.h>
#include <SomeOtherException.h>

#include <iostream>

using namespace std;

static int DoSomething(int argc) {
    try {
        switch (argc) {
        case 0:
            throw SomeException("some");
        case 1:
            throw SomeOtherException("some other");
        default:
            return 0;
        }
    }
    catch (const exception& ex) {
        cout << ex.what() << endl;
        return 1;
    }
}

int main(int argc, char**) {
    return DoSomething(argc);
}

原文由 Oğuzhan Türk 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题