8

I have the following code:

class A {
  private:
    int i;
 };

class B : public A {
 private:
  int j;
 };

When I check sizeof(B), it appears to be sizeof(base) + sizeof(derived). However, my understanding of inheritance is that the private members of a base class are not inherited. Why then are they included in the result of sizeof(B)?

isanae
  • 3,253
  • 1
  • 22
  • 47
srinuvenu
  • 475
  • 1
  • 5
  • 11
  • More general question: [Difference between private, public and protected inheritance in C++](http://stackoverflow.com/questions/860339/difference-between-private-public-and-protected-inheritance-in-c) – Kirill V. Lyadvinsky Jul 04 '11 at 08:22

7 Answers7

8

all member variables are inherited. the private protected public modifiers only alter who has access to those variables

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
6

You misunderstand what private does. In your code snippet it simply prevents non-members and non-friends of A from accessing i. It's just an access-control modifier. Instances of B will have data members of A, even though B won't have (direct) access to it.

An analogy that shows that this does in fact make sense:

class Human
{
protected:
    void UseCognitivePowers() { brain.Process(); }
private:
    Brain brain;
    // ...
};

class StackOverflowUserInSilico : public Human
{
private:
    void AnswerStackOverflowQuestion(int questionId)
    {
        // ...
        // magic occurs here
        // ...
        UseCognitivePowers();
    }
};

Even though brain is private in the Human class, StackOverflowUserInSilico will have a Brain since StackOverflowUserInSilico derives from Human. This is needed, otherwise the UseCognitivePowers() function won't work even though StackOverflowUserInSilico inherits the method from Human.

Of course whether subclasses of Human will actually take advantage of the UseCognitivePowers() method afforded to them by the Human class is a totally different matter.

In silico
  • 51,091
  • 10
  • 150
  • 143
5

You either misunderstand sizeof or your misunderstand the layout (in memory) of C++ objects.

For performance reason (to avoid the cost of indirection), compilers will often implement Derivation using Composition:

// A
+---+
| i |
+---+

// B
+---+---+
| A | j |
+---+---+

Note that if private, B cannot peek in A even though it contains it.

The sizeof operator will then return the size of B, including the necessary padding (for alignment correction) if any.

If you want to learn more, I heartily recommend Inside the C++ Object Model by Stanley A. Lippman. While compiler dependent, many compilers do in fact use the same basic principles.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • How exactly would this be implemented *without* doing it this way? (Vis a vis cost of indirection?) – Billy ONeal Jul 05 '11 at 04:57
  • @Billy: you could decide that the base classes are embedded via a pointer (created by `new` and removed by `delete`). Then the base class would always occupy 4 or 8 bytes (depending on the system). It would be somewhat better ABI wise, but much more costly. – Matthieu M. Jul 05 '11 at 06:19
  • M.: Actually, you cannot do that. If it is created as an automatic variable, then it has to exist in automatic storage. Using something like `new` would be putting it in dynamic storage. (Moreover, if all you had was a base class pointer you'd have no way to get back to the derived parts of the object -- the bases don't know about their derivers) – Billy ONeal Jul 05 '11 at 06:22
  • @Billy: Automatic variable does not mean *stack*, it means the memory is managed for you. Good point wrt getting from base to derived, but your forget that it's already solved for virtual inheritance (although it IS costlier). – Matthieu M. Jul 05 '11 at 06:24
  • Most inheritance is not virtual. I don't see how that solves anything. – Billy ONeal Jul 05 '11 at 06:25
  • @Billy: I didn't say it solved anything, I merely point out that the Standard does not mandate the object representation, this is a compiler writer decision, and the mechanism of virtual inheritance could be used for regular inheritance too, in which case `sizeof` would not be affected by the size of the base class. There is no C++ implementation that I know of who uses such a wasteful scheme though. – Matthieu M. Jul 05 '11 at 06:29
  • I think that falls under the category of things that aren't technically mandated, but are implied, by the standard, such as [`std::basic_string::operator`(brackets) returning a reference to a contained character type in a contiguous buffer](http://stackoverflow.com/questions/2256160/how-bad-is-code-using-stdbasic-stringt-as-a-contiguous-buffer). (Can't use [] above because the linkification eats it...) – Billy ONeal Jul 05 '11 at 06:48
  • @Billy: no idea, I am still not versed enough in Standardese to understand all the rules, so *deducing* them is quite out of reach :D – Matthieu M. Jul 05 '11 at 11:50
3

It is inherited - the derived class object will contain it, but it can't be accessed by member functions of the derived class.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • hey..thanks a lot..so the derived class inherits every thing,but the derived class public member functions cannot access the private members of base class ri8! – srinuvenu Jul 04 '11 at 08:03
3

This has already been answered in a few other answers: access specifiers restrict access, but the member attributes of the class are still inherited.

I just wanted to provide a rationale, as I usually learn better when I see the reasons for that. Basically, when you inherit from an type, the derived type contains a subobject of the base type, as small or large as the base might be. The reason for needing all of the member variables is that the derived object is a base object, and base level member functions can be called on it. Even if the derived type cannot access the private member attribute, the base methods that can be called on that object might still need to access it, so the members must be there:

class base {
   int x;
public:
   base() : x(0) {}
// ...
   void printout() {
      std::cout << x << std::endl;
   }
};
class derived : public base {
// ... assume that derived does not hide `printout`
};
int main() {
   derived d;
   d.printout();  // this requires access to d.base::x
}

This is only a simple example, and there are a few things that you can say here to argue that in some cases x can be made unneeded (we are overriding/hiding printout in the derived object...) but the language still allows you to access a hidden/overridden member method by qualifying, so d.base::printout() would still access printout at the base level, and that in turns requires x.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
0

Access specifiers (public/private/protected) don't affect inherited "object size" in anyway.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • They do. They control which members of the class will be visible in the drived class. – Alok Save Jul 04 '11 at 07:52
  • @iammilind: they do affect the size too. Because of an arcane rule that stipulates that they affect the layout of the class, changing a specifier might change the final class size... – Matthieu M. Jul 04 '11 at 08:09
  • Matthieu's right - 11.1-2 and 9.2-12 - vaguely recall discussions of this allow an each check re the member's offset to also indicate the protection level (i.e. if (some_class_member.offset >= first_private_offset) /* it's private */). It might have also been an attempt to facilitate use of some real or postulated run-time security mechanism (e.g. with hardware allowing fine-grained memory access control, lock/unlock privates as private member functions called/return). Anyway, alignment rules mean order can affect efficiency of padding. – Tony Delroy Jul 04 '11 at 08:47
0

i dont know what language this is, but when you inherit the original object still exists. that is why you can still call base.method()

BradLaney
  • 2,384
  • 1
  • 19
  • 28