1

There is an object who's members I need to find the size of. I am specifically asking for the object's size without it's v-table considered. Also, I cannot modify it, so I cannot take advantage of this answer.

Is there a provision for this in C++, beyond summing a hard-coded sizeof for each member?

I am aware that v-tables are not mandated by C++. I am also aware that anything I do with this information will be widely considered "bad form". This question is simply asking if it's possible, not endorsing the behavior.


It has come to my attention that I need to clarify this question. What I wanted to learn with this question was how to cast a parent to a child. That is, I wanted to preserve the child's v-table, but copy the parent's member variables: https://stackoverflow.com/a/31454039/2642059

The accepted answer does provide me the information I needed to do this. But, in-spite of behavior that I consider endemic to the worst of http://stackoverflow.com curiousguy points out a shortcoming of the accepted answer.

The extension from the accepted answer to multiple inheritance is patently obvious, but it is valid that the answer should include it. As a stopgap I've added a live example of how to deal with multiple inheritance: http://ideone.com/1QOrMz I will request that user2596732 updates his answer or I will add a supplementary answer to the question on how to deal with multiple inheritance.

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    I don't understand what you're asking. The object doesn't contain the v-table, it contains a pointer to the v-table. So if you don't count virtual inheritance, isn't the answer to your question (on most implementations) `sizeof(Object) - sizeof(void*)`? With virtual inheritance, you'll probably have something larger than a pointer within the class. – Praetorian Jul 15 '15 at 01:01
  • @Praetorian Yes, it is, but I actually thought the v-table was included in the object allocation, rather than just referenced in the object's allocation. It seems [Lightness Races in Orbit may believe there is more to it though](http://stackoverflow.com/questions/31418695/get-the-sizeof-objects-members#comment50810948_31418885). – Jonathan Mee Jul 15 '15 at 01:20
  • I think he may have misunderstood what you were asking. It does seem like you're asking for the sum of `sizeof` of each member in some class, which would then mean you're asking for way to exclude the vptr, padding etc. – Praetorian Jul 15 '15 at 05:40
  • @Praetorian I see you using the `sizeof(void*)` here is there something more official than that provided by c++ for the pointer size? – Jonathan Mee Jul 15 '15 at 10:36
  • 1
    As far as this question is concerned, no, there isn't. Otherwise, [`uintptr_t`](https://stackoverflow.com/a/1846648/241631) might be more appropriate. – Praetorian Jul 15 '15 at 14:06
  • @Praetorian Wow, I had no idea that existed; that's awesome. Still for the purposes of this problem, as you say, that's not really what we need. I guess `void*` it is. – Jonathan Mee Jul 15 '15 at 14:46
  • @Praetorian Not just virtual inheritance, just multiple inheritance would also complicate matters. But then it isn't clear what the matter is! – curiousguy Aug 01 '15 at 00:44
  • 1
    @curiousguy: I'm not sure why you've now popped up weeks later out of the blue on a solved question to comment on everything? – Lightness Races in Orbit Aug 01 '15 at 01:00
  • @LightnessRacesinOrbit 1) It's my right to "pop up weeks later(...)". I comment when I feel like commenting, because I think I have something to say. 2) The question is confusing. 3) Just because some answer is accepted doesn't imply **I** have to accept it. Criticising old accepted answers is allowed by the SO software so I do it when I feel like doing. (In fact the whole SO concept of letting the person who ask question judge the validity of answers is flawed at the basic level.) – curiousguy Aug 01 '15 at 01:05
  • 1
    @curiousguy: It looks like you're drunk or something and have come on here just to berate everyone with falsehoods for no reason. Did you have a goal? Or are you just here to be belligerent? Also note that jumping to the "I know my rights" defence when called out on your conduct is not going to win you any popularity contests. – Lightness Races in Orbit Aug 01 '15 at 01:06
  • @LightnessRacesinOrbit Ok, this is enough. Please stop before I feel the need to flag your attacks. – curiousguy Aug 01 '15 at 01:09
  • @LightnessRacesinOrbit Flagged. – curiousguy Aug 01 '15 at 01:13
  • @curiousguy: I'd beaten you to that by several minutes. – Lightness Races in Orbit Aug 01 '15 at 01:13
  • For what I believe is what you want to do (`memcpy`), would you only need the size of the class without vptrs? I don't think so. You also want to determine the location of the vptrs, right? – curiousguy Aug 03 '15 at 17:25
  • @curiousguy Yes, exactly. Or even if the compiler used v-tables (as mentioned in the question.) It appears that gcc and Visual Studio both use a v-table pointer before the member variables. You can see in the linked example in the question that in the case of multiple inheritance both gcc and Visual Studio will arrange the object layout: [Parent1vTable, Parent1Members, Parent2vTable, Parent2Members]. – Jonathan Mee Aug 03 '15 at 17:37
  • @JonathanMee 1) **In practice all compilers use vtables.** The common implementation is both reasonably memory efficient and runtime efficient except maybe in rare and very complicated cases involving lots of base classes, MI, and virtual inheritance. Only an interpreter designed for debugging would dare to use an alternate implementation of polymorphism. 2) So you can count on it, except you can't count on much because details vary. – curiousguy Aug 03 '15 at 17:52
  • 3) I have once used a C++ compiler which put the vptr at the end, but at the beginning is the most common choice. GCC follows the "Itanium C++ ABI" which says vptr is at offset zero. You can check your compiler's documentation. You can check the official ABI on your platform. You could also discover at runtime the location of the vptr with pointer comparisons: the vptr is that "empty" place where no member is located. – curiousguy Aug 03 '15 at 17:58
  • 4) I wonder if you understand what vptr means; the term vptr does not designate any pointer to some metainfo called vtable, a vptr is a **pointer located at a fixed offset** (often offset 0) inside some datatype, just like struct members. A vptr is used to determine the runtime properties of an object, including virtual member function calls, implement `typeid`, `dynamic_cast` and also determine the location of virtual bases. The vptr and data members are obviously contiguous. – curiousguy Aug 03 '15 at 18:11
  • @curiousguy Your comments sound more official than the hand waving that we have done on this topic. But at the end of the day the truth is, we really can't count on anything related to compiler implementation. Any assumptions made, may not be compatible even across compiler versions. That should probably be mentioned in the accepted answer. If we don't have a reaction to my comment on [user2596732](http://stackoverflow.com/users/2596732/user2596732)'s answer by tomorrow I will update and include this comment. – Jonathan Mee Aug 03 '15 at 18:13
  • 5) I hope you understand that `memcpy` of arbitrary datatypes is not well defined. You are guaranteed that you can use `memcpy` on C like types (PODs) but that's about all. Copying the bytes of a polymorphic type has undefined behaviour. – curiousguy Aug 03 '15 at 18:31
  • @curiousguy I had hoped to avoid exactly this conversation with the statement in the original question: "I am also aware that anything I do with this information will be widely considered "bad form". This question is simply asking if it's possible, not endorsing the behavior." Though since we've had to go here, hopefully this will benefit someone else reading the question. – Jonathan Mee Aug 03 '15 at 18:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/85035/discussion-between-curiousguy-and-jonathan-mee). – curiousguy Aug 03 '15 at 20:17

3 Answers3

3

Nope.​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Lol, that was easy enough. I was hoping to find a way to use, [offsetof](http://en.cppreference.com/w/cpp/types/offsetof). but it seems like there isn't a virtual class equivalent. – Jonathan Mee Jul 14 '15 at 22:58
  • @JonathanMee There is no such thing as a virtual class. A class with at least one virtual function is a polymorphic class. – curiousguy Aug 01 '15 at 00:46
  • @curiousguy I agree with that... I'm pretty sure that everyone does? I think I really need to clarify my question as it is causing a lot of confusion: http://stackoverflow.com/questions/31418695/get-the-sizeof-objects-members#comment50827229_31418885 – Jonathan Mee Aug 01 '15 at 23:11
  • @curiousguy I have updated the question. If you still feel that it is unclear what I'm asking please let me know. – Jonathan Mee Aug 02 '15 at 12:36
  • 1
    @JonathanMee OK, more context helps. Now I think I get it. – curiousguy Aug 03 '15 at 17:21
2

In order to discover the layout of a polymorphic object, you can compare pointers to member objects; the simple demonstration program "draws" the layout of an object with symbols:

  • a lowercase letter is the name of a data member
  • an uppercase letter is the name of a base class
  • * indicates part of the object that do not belong to any member subobject or base class subobject

There is a symbol for each byte (a char is a byte by definition).

The vptr(s) must be in the "empty" space, the space not allocated for data members.

Type definitions are:

struct T { 
    virtual void foo();
    int i;
};

struct U { 
    virtual void bar();
    long long l;
};

struct Der : T, U { 
};

struct Der2 : virtual T, U { 
};

struct Der3 : virtual T, virtual U { 
};

Output is:

sizeof void* is 4
sizeof T is 8
sizeof i is 4
i is at offset 4
layout of T is 
****iiii
sizeof U is 12
sizeof U::l is 8
l is at offset 4
layout of U is 
****llllllll
sizeof Der is 20
Der::i is at offset 4
Der::l is at offset 12
Der::T is at offset 0
Der::U is at offset 8
layout of Der is 
TTTTiiiiUUUUllllllll
sizeof Der2 is 20
Der2::i is at offset 16
Der2::l is at offset 4
Der2::T is at offset 12
Der2::U is at offset 0
layout of Der2 is 
UUUUllllllllTTTTiiii
sizeof Der3 is 24
Der3::i is at offset 8
Der3::l is at offset 16
Der3::T is at offset 4
Der3::U is at offset 12
layout of Der3 is 
****TTTTiiiiUUUUllllllll

See https://ideone.com/g5SZwk

Because we know the compiler is using vptrs, the locations of the vptrs are obvious in these "drawings".

About inheritance in C++

Non-virtual inheritance

When virtual inheritance is not used, the base class subobjects inheritance graph is always a tree rooted in the most derived class, even when the subtyping graph is not a tree:

struct Repeated {
    virtual void f();
    virtual void g();
};
struct Left : Repeated {
    void g();
};
struct Right : Repeated {
    void g();
};
struct Bottom : Left, Right {
    void f();
};

The subtyping graph is:

           Left
         /      \
Repeated          Bottom
         \      /
           Right

The subobject graph is:

Left::Repeated ---  Left
                         \
                          Bottom
                         /
Right::Repeated --- Right

This is crucial effect of non-virtual inheritance: the graphs don't always match. If you don't understand that you don't understand non-virtual inheritance!

This implies that conversions from Bottom* to Repeated* are ambiguous.

In this example:

  • Bottom::f() overrides both Left::Repeated::f() and Right::Repeated::f() at the same time.
  • Left::Repeated::g() is overridden by Left::g()
  • Right::Repeated::g() is overridden by Right::g()

Here the lookup of the name g in Bottom would fail with an ambiguity, so it would be an error to use an unqualified g in Bottom.

Virtual inheritance

When virtual inheritance is used, the base class subobjects inheritance is an acyclic directed graph with the most derived class as a unique terminal:

struct Unique { virtual void f(); };
struct Left : virtual Unique { void f(); };
struct Right : virtual Unique { void f(); };
struct Bottom : Left, Right { void f(); };

Here all other f() declarations override Unique::f().

Here the subobject graph matches the subtype graph:

           Left
         /      \
  Unique         Bottom
         \      /
           Right
curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • Hmmm... I haven't seen `virtual` as an inheritance keyword before. I can't understand why `struct Der3 : virtual T, virtual U` requires 24 bytes instead of 20 bytes. Can you explain? – Jonathan Mee Aug 03 '15 at 19:41
  • @JonathanMee I am preparing a long answer about "virtual". – curiousguy Aug 03 '15 at 20:13
  • I take that back I have seen `virtual` but I've only seen it used to break diamond inheritance conflict or parent function collision. Is there another use for it? Why would you mark both of your parents `virtual`? – Jonathan Mee Aug 04 '15 at 10:58
  • @JonathanMee 1) There is no "diamond inheritance conflict", there are only resolvable ambiguities. The aim of virtual inheritance is not to resolve ambiguities but to suppress the cause of the ambiguity by having one (virtual) base class of a given type. Virtual bases are used mostly for interface classes, and non virtual are often used for implementation classes. They are different design choices. – curiousguy Aug 04 '15 at 12:43
  • 2) Parent function collision: do you mean when two base classes have functions with the same name? virtual is of no use here. 3) I have updated my answer with some explanation of virtual vs. non virtual base classes. – curiousguy Aug 04 '15 at 12:44
  • 4) A virtual base is a bit like a virtual function, it can be "overridden" (almost). A non virtual base is like a non virtual function, you can't change it. IOW, a virtual base as a dynamic, flexible link; a non virtual base as a static, fixed link. They are semantically different feature, like a member of type `int` and a member of type `const int` have different semantics. – curiousguy Aug 04 '15 at 13:04
  • Thanks for the update, that's greatly cleared things up for me. I [have asked user2596732 to update his answer](http://stackoverflow.com/questions/31418695/get-the-sizeof-objects-members/31795025#comment51565313_31418885) if he does not within the week I will accept yours. – Jonathan Mee Aug 04 '15 at 22:08
0

sizeof(Class) only includes a VTable pointer.

class A
{
public:
    int a = 2;
    int b = 2;
    virtual void x() {
    };

    virtual void y() {
    };
};


class B : public A
{
public:
    int c = 2;
    int d = 2;
    virtual void y() {
    };
};

class C : public A
{
public:
    int c = 2;
    int d = 2;
    int e = 2;
    virtual void x() {
    };
};

So for this example,

    cout << sizeof(A)-sizeof(void*) << endl;
    cout << sizeof(B)-sizeof(void*) << endl;
    cout << sizeof(C)-sizeof(void*) << endl;

should give you the correct answer. 8 16 20

user2596732
  • 47
  • 1
  • 8
  • Interesting... Seems like this is a bit implementation dependent, but I suppose we could find a pointer's size and that should be all that is added to the size of the object... hopefully ;) – Jonathan Mee Jul 14 '15 at 23:09
  • I can't even count the ways this is wrong. You're even making assumptions about the vptr's size, let alone padding between members, any other metadata the implementation cares to add, and so on and so forth. – Lightness Races in Orbit Jul 14 '15 at 23:47
  • @LightnessRacesinOrbit I agree that he's assuming the size of a pointer is `4`, but wouldn't all metadata be reflected in `sizeof(X)`? – Jonathan Mee Jul 15 '15 at 01:22
  • @JonathanMee: Yes, it would, and that's precisely the problem with this answer! (The question asks for the sum of `sizeof(members(X)[i])`) – Lightness Races in Orbit Jul 15 '15 at 10:49
  • @LightnessRacesinOrbit Oh, [blush] I meant to ask for this. Should I edit my question? I don't want to make your answer sound dumb if there is an obvious answer. – Jonathan Mee Jul 15 '15 at 10:51
  • @user2596732 I'd like to accept this answer, but you've got to check your v-table pointer sizes first. Rather than just subtracting a bare-constant `4` can you just subtract `sizeof(void*)` in your answer? – Jonathan Mee Jul 15 '15 at 14:48
  • @LightnessRacesinOrbit "_The question asks for the sum of sizeof(members(X)[i]))_" then compute `sizeof(members(X)[i]))` – curiousguy Aug 01 '15 at 00:46
  • @LightnessRacesinOrbit If you need a sum, use the `+` operator. – curiousguy Aug 01 '15 at 01:01
  • 1
    @curiousguy: Not sure whether you actually read the question but it explicitly exempted your solution (_"beyond summing a hard-coded sizeof for each member?"_), so I don't know what point you're trying to make. You responded to a criticism of this answer. Do you disagree with it? – Lightness Races in Orbit Aug 01 '15 at 01:03
  • @LightnessRacesinOrbit "_it explicitly exempted your solution_" So? How is a refusal of the solution in the question asking for a solution a reason to not propose the refused solution? **There is no credible reason given for the refusal of that solution, so I have the right to proposed a refused solution.** Rejecting solutions is the sign of a bad question. **Bad questions are of no use of the community.** – curiousguy Aug 01 '15 at 01:12
  • 1
    @curiousguy: Please stop trolling this Q&A citing your "rights" as the only justification. Thank you. – Lightness Races in Orbit Aug 01 '15 at 01:13
  • @LightnessRacesinOrbit Please stop claiming I am trolling. – curiousguy Aug 01 '15 at 01:14
  • @curiousguy: Okay, no problem. I agree that there is no need to make this observation again. It's covered by the first time I said it. – Lightness Races in Orbit Aug 01 '15 at 01:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/84858/discussion-between-curiousguy-and-lightness-races-in-orbit). – curiousguy Aug 01 '15 at 01:16
  • @curiousguy: No, let us instead bring this off-topic nonsense to a complete and final stop. Good night. – Lightness Races in Orbit Aug 01 '15 at 01:18
  • @LightnessRacesinOrbit As long as you withdraw your attacks, yes. – curiousguy Aug 01 '15 at 01:20
  • @user2596732 Aside from spewing downvotes and destructive comments [curiousguy has requested that the answer also address multiple inheritance](http://stackoverflow.com/questions/31418695/get-the-sizeof-objects-members#comment51447708_31418695) can you take care of that in your answer? If not I can add a supplementary answer. – Jonathan Mee Aug 02 '15 at 12:35
  • @user2596732 I've sharpened my question to remove ambiguity. Since I have accepted your answer I would like if you could provide an updated answer. As it stands now though this answer is wrong, so if I don't get a response from you in a week I will move to accept [curiousguy](http://stackoverflow.com/users/963864/curiousguy)'s answer. – Jonathan Mee Aug 04 '15 at 22:07
  • 1
    @JonathanMee , you can move the answer, his answer is much better then mine. – user2596732 Aug 05 '15 at 02:00