Does this pointer adjustment occur for non-polymorphic inheritance?

﹥>﹥吖頭↗ 提交于 2019-12-06 08:10:18

Layout of objects is not specified by the language. From the C++ Draft Standard N3337:

10 Derived Classes

5 The order in which the base class subobjects are allocated in the most derived object (1.8) is unspecified. [ Note: a derived class and its base class subobjects can be represented by a directed acyclic graph (DAG) where an arrow means “directly derived from.” A DAG of subobjects is often referred to as a “subobject lattice.”

6 The arrows need not have a physical representation in memory. —end note ]

Coming to your question:

Would the following function call require this pointer adjustment?

It depends on how the object layout is created by the compiler. It may or may not.

In your case, since there are no member data in the classes, there are no virtual member functions, and you are using the member function of the first base class, you probably won't see any pointer adjustments. However, if you add member data, and use a member function of the second base class, you are most likely going to see pointer adjustments.

Here's some example code and the output from running the code:

#include <iostream>

struct Base1 {
   void b1()
   {
      std::cout << (void*)this << std::endl;
   }
   int x;
};

struct Base2 {
   void b2()
   {
      std::cout << (void*)this << std::endl;
   }
   int y;
};

struct Derived : public Base1, public Base2 {
   void derived() {}
};

int main()
{
   Derived d;
   d.b1();
   d.b2();
   return 0;
}

Output:

0x28ac28
0x28ac2c

This is not just compiler-specific but also optimization-level-specific. As a rule of thumb, all this pointers are adjusted, only sometimes it is by 0 as would be your example in many compilers (but definitely not all — IIRC, MSVC is a notable exception). If the function is inlined and does not access this, then the adjustment may be optimized out altogether.

Using R Sahu's method for testing this, it looks like the answer for gcc, clang, and icc is yes, this pointer adjustment occurs, unless the base class is the primary base class or an empty base class.

The test code:

#include <iostream>

namespace {
struct Base1
{
    void b1()
    {
        std::cout << "b1() " << (void*)this << std::endl;
    }

    int x;
};

struct Base2
{
    void b2()
    {
        std::cout << "b2() " << (void*)this << std::endl;
    }

    int x;
};

struct EmptyBase
{
    void eb()
    {
        std::cout << "eb(): " << (void*)this << std::endl;
    }
};

struct Derived : private Base1, Base2, EmptyBase
{
    void derived()
    {
        b1();
        b2();
        eb();
        std::cout << "derived(): " << (void*)this << std::endl;
    }
};
}

int main()
{
    Derived d;
    d.derived();
}

An anonymous namespace is used to give the base classes internal linkage. An intelligent compiler could determine that the only use of the base classes is in this translation unit and this pointer adjustment is unnecessary. Private inheritance is used for good measure but I don't think it has real significance.

Example g++ 4.9.2 output:

b1() 0x7fff5c5337d0
b2() 0x7fff5c5337d4
eb(): 0x7fff5c5337d0
derived(): 0x7fff5c5337d0

Example clang 3.5.0 output

b1() 0x7fff43fc07e0
b2() 0x7fff43fc07e4
eb(): 0x7fff43fc07e0
derived(): 0x7fff43fc07e0

Example icc 15.0.0.077 output:

b1() 0x7fff513e76d8
b2() 0x7fff513e76dc
eb(): 0x7fff513e76d8
derived(): 0x7fff513e76d8

All three compilers adjust the this pointer for b2(). If they don't elide the this pointer adjustment in this easy case then they very likely won't ever elide this pointer adjustment. The primary base class and empty base classes are exceptions.

As far as I know, an intelligent standards conforming compiler could elide the this pointer adjustment for b2() but it's simply an optimization that they don't do.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!