Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is pointer to base always <= pointer to derived class?

I wonder if it's guaranteed by the C++ standard that single inheritance make the object "grow" upward, that is given a class Base and a class Derived: public Base, and a pointer Derived* ptr, the result of dynamic_cast<Base*>(ptr) will be always numerically smaller or equal to ptr.

like image 406
Lorenzo Pistone Avatar asked Apr 08 '12 11:04

Lorenzo Pistone


2 Answers

No. Memory layout is implementation detail.

That being said, I'm not aware of any implementation that doesn't actually do that, assuming there are no virtual functions. If you introduce a virtual function in Derived (but there were none in Base), the virtual table pointer could (depending on the implementation) be placed before the Base fields (making the Base* greater than Derived*).

Clarification:

The example above is Visual C++ specific. You can check it out using the following code:

class Base {
    int X;
};

class Derived : public Base {
    virtual void f() {
    }
    int Y;
};

int main() {

    Derived d;
    Derived* d_ptr = &d;
    Base* b_ptr = dynamic_cast<Base*>(d_ptr); // static_cast would be enough BTW.

    bool base_smaller_or_equal = (ptrdiff_t)b_ptr <= (ptrdiff_t)d_ptr;

    return 0;

}

The base_smaller_or_equal will be false under Visual C++. Judging on @enobayram's comment, it should be true under GCC.

In any case, it is an implementation detail and not to be relied upon.

like image 88
Branko Dimitrijevic Avatar answered Oct 05 '22 07:10

Branko Dimitrijevic


No.

However, there is no need to panic yet. This kind of question, though not fully addressed in the Standard C++ is answered by the ABI document the compiler will follow. Many compilers such as gcc, Clang or icc (but not VC++) follow the Itanium ABI.

In the Itanium ABI, as long as you have single non-virtual inheritance and the Base class has a virtual method, then Derived and Base will always have the same address.

That being said, this is an implementation you actually need not worry about. You can perfectly write C++ Standard compliant code and still manage your use cases. The thing is that C++ allows you to cast any pointer to void* and char* (the latter as a specific exception to non-aliasing) and back. The only thing you need to worry about is that when you case a Base* to a void* you need cast it back to a Base* and not a Derived*. That is, the type you put in and the type you get back should match.

It is much more difficult however to know (for sure) the size of an object. This requires the application of sizeof to the current dynamic type of the object and there is not facility to get it virtually.

My advice would be to actually instrument your base class so:

class Base {
public:
  char const* address() const { return (char const*)dynamic_cast<void const*>(this); }
  size_t offset() const { return this->address() - (char const*)this; }

  virtual size_t size() const { return sizeof(Base); } // to be overriden

  virtual ~Base() {}
};

This helps getting all the information you need (see demo). Note that with the Itanium ABI offset() will always return 0, but at least you are not assuming here.

like image 27
Matthieu M. Avatar answered Oct 05 '22 06:10

Matthieu M.