1

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

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. 这三个功能有区别吗?

我问这个是因为我只根据我对事情如何运作的了解对这个问题有一个假设。因为我的一些假设在过去被证明是错误的,所以我想在这里确定一下。

如果已经有人问过这个问题,请原谅我,如果可能的话,请指出适当的答案。

PS:您不需要对“过早的优化是万恶之源”或关于分析的任何提示。我可以使用我正在开发的编译器来分析这个问题,但我所针对的程序可以使用任何符合要求的编译器进行编译。因此,即使我无法确定任何差异,它们仍可能存在。

4

3 回答 3

1

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

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

于 2013-07-16T15:41:30.297 回答
1

我的理解是——没有区别。

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

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

于 2013-07-16T15:42:17.907 回答
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;在这种情况下,它将只使用常量,并且不会有任何访问权限。事实上,担心这一级别的优化是错误的,因为编译器会为你处理它,比你能做的更好。

于 2013-07-16T16:03:37.407 回答