【问题标题】:c++ classes without .cpp file without inline functions?没有.cpp文件的c ++类没有内联函数?
【发布时间】:2014-03-19 09:59:19
【问题描述】:

我已经问过一个类似的问题,但这个问题有点不同

我不想为每个简单的 c++ 类编写一个 .cpp 文件。

当我在单个 .hpp 文件中编写类定义和声明时,链接器会抱怨没有在类主体内实现或使用内联键盘定义的成员函数的多个定义。

例如,这将起作用,但成员函数将变为内联:

// log.hpp file
#pragma once
#include<iostream>

class log {
  private:
    static int m_cnt = 0;
  public:
    void log();
};

inline void log::log() {
    std::cout << ++m_cnt << std::endl;
}

所以我使用模板来摆脱链接器的抱怨,并希望成员函数不会变成内联(是吗?):

// log.hpp file
#pragma once
#include<iostream>

template<typename T>
class log_t {
  private:
    static int m_cnt = 0;
  public:
    void log();
};

template<typename T>
void log_t<T>::log() {
    std::cout << ++m_cnt << std::endl;
}

// some random type (int)
typedef log_t<int> log;

然后我可以简单地在多个 .cpp 文件中使用日志类而不会引起链接器投诉。

即使我使用这种方法,成员函数也会内联吗?

【问题讨论】:

  • "我不想为每个简单的 c++ 类编写一个 .cpp 文件。"为什么?如果您不想使用 inline,无论如何都不要使用...这并不意味着编译器不会这样做,如果它认为它会生成更好的代码。
  • 不,您可以在多个文件中包含定义,但函数体应保留在 .cpp 中(对于内联函数,它不能)。这是非常基于意见的,但 IMO 没有。简单与否,简短与否都没有关系。 .H 用于定义,.CPP 用于实现。对于模板或内联函数,您必须将实现放在 .H 中,但对于其他所有内容,请保留此区别。
  • @amin 在编写 C++ 代码时,像 C# 开发人员一样思考可能无济于事。
  • inline 不是指令,只是提示/注释。编译器自己决定内联什么。有时,相同的函数可以在某些调用站点内联,而在其他站点不能内联,只需让编译器完成它的工作。您可以使用-Os 进行编译以减少内联。

标签: c++ templates c++11 linker


【解决方案1】:

虽然将所有内容放在单个 .h 文件中通常是不好的做法,但在某些情况下这样做更方便。尤其是在定义小类时,以及在代码可以快速更改的原型阶段。

我真的不建议使用模板来解决链接问题,因为它会减慢编译速度并导致混乱:这就像创建一个函数,当你除了那个参数总是具有相同的值时。无论如何,输入inlinetemplate&lt;typename T&gt; 短;)

因此,您要么必须在类主体中定义您的方法,要么使用inline 注释它们(如果在主体之外定义)。这是等效的,因为 C++ 会自动将inline 添加到类主体中定义的方法中。

您关心的似乎是生成的代码,您可能想知道二进制文件是否会增长太多。但是一切都很酷,因为不管inline 注释如何,编译器都会查看每个函数调用并决定是内联它还是生成调用。这意味着同一函数有时可能会被内联(如果在循环中调用),有时也会被调用。

不同的编译器有不同的启发式方法,但是inline 关键字对编译器的决定并没有特别大的影响。您可以使用__forceinline__attribute__((always_inline)) 之类的东西来更强烈地要求内联函数,但即使这样也不能保证对函数的所有调用都会被内联。相反,您可能对__attribute__(noinline) for gcc 感兴趣,它(几乎)不会内联调用:

inline __attribute__(noinline) void log::log() // gcc
{
    std::cout << ++m_cnt << std::endl;
}

如果您想真正了解代码中发生了什么,您可以使用-Winline 查看inline 函数何时未内联。

您还可以使用-Os-Oz 优化级别来更改内部编译器阈值,从而减少内联。

您可能感兴趣的相关问题:herehere

【讨论】:

    【解决方案2】:

    一般来说,将每个实现都放在头文件中是一个坏主意,因为它会导致大型项目的编译时间很长。使用模板来避免这种情况也不是一个好主意,因为它不会改变这一事实。

    inline 只是对编译器的提示;它可能会忽略它并内联您的模板代码。简而言之:您的问题的答案并不重要。标准/最佳方式是使用 .cpp 文件来实现,即使您想避免它。

    【讨论】:

    • 我只是想知道这是否可能?所以如果我使用内联键盘编译器可能会或可能不会内联函数?
    • “一般来说,在头文件中包含每个实现都是一个坏主意”——告诉boost库。
    • Jefffrey,他们正在模板化他们的东西,所以没有其他选择 ;-)(嗯,有,但它涉及将头文件拆分为接口和实现头文件,并实例化每个常用的组合.cpp 文件中的模板参数...)
    • 阿明,这是可能的。但inline 只是对编译器的提示。它可能会内联您未声明为 inline 的内容,或者尽管声明了 inline 的内容,但仍会生成单独的函数。
    猜你喜欢
    • 1970-01-01
    • 2011-04-28
    • 1970-01-01
    • 2011-02-14
    • 2020-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多