返回嵌套对象的成员是否有任何惩罚?

时间:2013-07-16 15:35:36

标签: c++ this class-members

考虑以下代码,关于成员的嵌套访问:

struct A
{
  size_t m_A;
};
struct B
{  
   A m_A_of_B;
};
class D
{
  B instance_B;
  A instance_A;
  size_t m_D;
public:
  size_t direct (void) { return m_D; }
  size_t ind1 (void) { return instance_A.m_A; }
  size_t ind2 (void) { return instance_B.m_A_of_B.m_A; }
};

我可以想象两种不同的情况:

1。没有区别

根据我的理解,应该没有区别,因为所有函数都返回一个值,该值在类内存布局中具有与this /相关的编译时常量位置。

我希望编译器能够识别它。

因此,我认为从上面所示的嵌套结构(甚至更深层次)中返回成员不会受到惩罚。

2。指针间接

有可能整个"间接"在这里进行。 以ind2为例:

取这个 - >获取instance_B的相对位置 - >获取m_A_of_B的相对位置 - >返回m_A


问题

  1. 编译器是否需要处理这种嵌套访问?
  2. 这三个功能有什么不同吗?
  3. 我问这个问题,因为我只从我所知道的事情如何运作中对这个问题做出了假设。因为我的一些假设在过去被证明是错误的,所以我想在这里要求确定。

    对不起,如果已经提出要求,请尽可能指出适当的答案。

    PS:你不需要提供任何关于"过早优化是所有邪恶的根源"或者关于分析。我可以使用我正在开发的编译器来描述这个问题,但我所针对的程序可以使用任何符合标准的编译器进行编译。因此,即使我无法确定任何差异,它们仍可能存在。

3 个答案:

答案 0 :(得分:1)

只要对象是直接成员(因此,不是指针或引用成员),就没有开销,编译器只计算适当的偏移量,无论你是否有一个,两个,三个或五十四个嵌套级别[假设你使用一个相当理智的编译器 - 正如评论所说,在这种情况下,没有什么能阻止某些顽固的编译器生成可怕的代码 - 这适用于许多情况下,你可以通过一些经验猜测编译器会做什么 - 很少有东西其中C ++标准规定编译器不能添加额外的代码,这些代码不能做任何特别有用的代码]。

显然,引用或指针成员会有读取实际对象地址的开销。

答案 1 :(得分:1)

我的理解是 - 没有区别。

如果你有(例如)堆栈上的D对象,那么访问任何成员或嵌套成员只是一个堆栈偏移量。如果D对象在堆上,那么它是一个指针偏移量,但不是真的不同。

这是因为D对象直接包含其所有成员,每个成员直接包含自己的成员。

答案 2 :(得分:1)

标准对此没有任何限制。编译器编写器 例如,一个真正扭曲的头脑会产生一个循环 在每个功能的开头都没有做任何事情 循环次数取决于数量 函数名中的字母。完全符合,但......我宁愿 怀疑他的编译器有很多用户。

在实践中,编译器只是(几乎)不可想象 计算出每个子对象的地址;例如在英特尔,做 类似的东西:

D::direct:
    mov eax, [ecx + offset m_D]
    return

D::ind1:
    lea ebx, [ecx + offest instance_A]
    mov eax, [ebx + offset m_D]
    return

D::ind2:
    lea ebx, [ecx + offset instance_B]
    lea ebx, [ebx + offset m_A_of_B]
    mov eax, [ebx + offset m_D]
    return

事实上,我见过的所有编译器都可以解决问题 完全布局直接包含的对象,并且会 生成类似的东西:

D::direct:
    mov eax, [ecx + offset m_D]
    return

D::ind1:
    mov eax, [ecx + offset instance_A + offset m_D]
    return

D::ind2:
    mov eax, [ecx + offset instance_A + offset m_A_of_B + offset m_D]
    return

(方括号中的偏移量的增加发生在 汇编程序;表达式对应一个常量 在实际可执行文件的指令内。)

所以回答你的问题:1是它完全是 编译器依赖,2是在实际操作中,会有 绝对没有区别。

最后,您的所有功能都是内联的。它们很简单 足够的,每个编译器内联它们,至少与任何 激活优化程度。并且一旦内联, 优化器可能会找到其他优化:它可能会 检测到您初始化D :: instance_B :: m_A_of_B :: m_A 例如,一个常数;在这种情况下,它只会使用 不变,并且不会有任何访问权限。事实上, 你担心这种优化水平是错误的,因为 编译器会为你做好照顾,比你更好。