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

C++ 返回嵌套对象的成员是否有任何惩罚?,c++,this,class-members,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) {

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

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中:

获取此->获取实例的相对位置->获取实例的m_A_的相对位置->返回m_A


问题
  • 这是否取决于如何处理嵌套访问
  • 这三种功能有什么不同吗
  • 我这样问是因为我对这个问题只有一个假设,从我所知道的事情是如何运作的。因为我的一些假设在过去被证明是错误的,所以我想在这里确认一下

    对不起,如果已经有人问过这个问题,请在可能的情况下给我指出适当的答案


    PS:您不需要给出任何关于“过早优化是万恶之源”或关于评测的提示。我可以用我正在开发的编译器来描述这个问题,但是我的目标程序可以用任何符合标准的编译器来编译。因此,即使我无法确定它们可能仍然存在的任何差异。

    只要对象是直接成员(因此,不是指针或引用成员),编译器只计算适当的偏移量,不管您有一个、两个、三个或五十四个嵌套级别假设你使用一个合理的SunEng-编译器,正如评论所说的,在这种情况下,没有什么可以阻止一些固执的编译器产生可怕的代码。这在很多情况下都是适用的,你可以用一些经验来猜测编译器会做什么——C++标准规定编译器不能增加额外的Co的东西很少。没有做任何特别有用的事情]


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

    我的理解是-没有区别

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


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

    标准对此没有任何限制。编译器编写器 比如说,一个真正扭曲的思维可以产生一个循环 它在每个函数的开头都不做任何事情 通过循环的次数取决于循环次数 函数名中的字母。完全一致,但…我宁愿 我怀疑他的编译器会有很多用户

    在实践中,很难想象编译器 计算每个子对象的地址;例如,在Intel上,执行 比如:

    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 例如,常数;在这种情况下,它将只使用 常数,而且不会有任何访问权限。事实上, 您担心这种优化级别是错误的,因为
    编译器会比你更好地为你处理它。

    如果程序可以用任何符合标准的编译器编译,那么你在这里得到的任何答案都可能是错误的,因为标准没有对如何实现这类事情进行限制。这实际上是对我的第一个问题的回答。任何编译器都没有开销我知道,但是标准中也没有任何东西阻止编译器首先计算每个子对象(和嵌套子对象)的实际地址,然后通过它们引用。@添加了JamesKanze澄清语句。下一个改进是指出函数是内联的,编译器将内联它们,这意味着为其中任何函数生成的确切代码将取决于周围的代码和编译器的优化算法。(换句话说,这个问题没有答案,但不需要答案,因为它完全不相关。)一个答案,我真的很感激:详细、清晰,并提供额外的信息