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
.
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*
).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With