【问题标题】:Class overhead in C++C++ 中的类开销
【发布时间】:2016-06-08 18:38:29
【问题描述】:

我有一门课,例如:

class Vehicle {
  public:
    Vehicle(int handle);

    // Methods that use the handle e.g.:
    Color getColor() {
        return VEHICLE::GET_COLOR(handle);
    }

  protected:
    int handle;
};

我不知道这个例子对你是否有意义,但我围绕这些句柄构建了几个包装类,以获得更 OOP 风格的编码。

所以我现在的问题是,当我将 Vehicle 对象传递给仅将车辆句柄传递给其他方法时,会有多少开销?

【问题讨论】:

  • 不太可能,您可能会通过 handle 引入开销
  • 您需要更具体的数据。记住 Hoare 的格言:过早的优化是万恶之源。使用分析器而不是猜测来衡量性能。 (其他因素也会起作用,例如您是按值传递还是按引用传递、实际类有多大、您使用的优化设置等)
  • Knuth:“程序员浪费大量时间思考或担心程序中非关键部分的速度,而在考虑调试和维护时,这些提高效率的尝试实际上会产生强烈的负面影响. 我们应该忘记小的效率,比如大约 97% 的时间:过早的优化是万恶之源。但我们不应该放弃关键的 3% 的机会。”
  • 您将通过比较编译器为您的平台为每个实现生成的目标代码来查看开销。

标签: c++ performance class oop


【解决方案1】:

如果您将类拆分为头文件和源文件并在其他编译单元中使用它,则会由于调用构造函数而产生少量开销。

为了解决这个问题,你的构造函数的定义必须放在你的头文件中,以便编译器可以内联它。

您可以通过更改类声明来做到这一点:

class Vehicle {
public:
    Vehicle(int handle)
     : handle(handle)
    {
    }
...

或者将定义放在你的头文件中并用inline关键字装饰它。

class Vehicle {
public:
    Vehicle(int handle);
...
}

inline Vehicle::Vehicle(int handle)
 : handle(handle)
{
}

请注意,不能保证您的函数会被内联,但可能每个主要的编译器都能做到这一点。

还要注意,构造函数中的额外工作,例如handle - 1,也很可能会导致开销。

如果你的类是多态的或更大的,可能会有额外的开销。

【讨论】:

  • “由于对构造函数的调用,会有一点开销”可能。我希望 LTO 能够启动并内联对简单构造函数的任何调用,即使它们是在不同的 TU 中定义的。
【解决方案2】:

优化编译器将确保这种简单的调用转发没有开销。理想情况下,核心类(由包装器使用)应该在同一个模块(DLL/SO)中,否则链接器/优化器可能没有什么帮助。

但是,对于这样围绕核心类的瘦包装器,即使在共享库场景中,编译器也会简单地调用核心方法,从而从调用站点消除包装器类方法。

【讨论】:

    猜你喜欢
    • 2013-03-19
    • 2011-07-21
    • 1970-01-01
    • 2011-05-05
    • 2012-07-27
    • 2012-04-03
    • 2010-10-29
    • 2010-12-31
    • 2011-10-04
    相关资源
    最近更新 更多