2

I am reading -- 3.4 Inheritance and the Data Member. It says "the language guarantee of the integrity of the base class subobject within the derived class." Also it gives an example as follows:

class Concrete1
{
private:
    int val;
    char bit1;
};

class Concrete2 : public Concrete1
{
private:
    char bit2;
};

class Concrete3 : public Concrete2
{
private:
    char bit3;
};

The Concrete1 object model is:

4 bytes for val, 
1 byte for bit1,
3 bytes padding. 
// 8 bytes in total.

The Concrete2 object model is:

8 bytes for Concrete1 subobject, 
1 byte for bit2,
3 bytes padding. 
// 12 bytes in total. 

Similarly, the Concrete3 object model is 16 bytes.

Concrete2 object member bit2 doesn't use the padding part of Concrete1 subobject, that's why it is 12 bytes. But when I tried the example in gcc 4.4.7, Concrete2 object and Concrete3 object are the same size as Concrete1 object -- 8 bytes. So I'm guessing gcc uses the padding part of Concrete1 object to store bit2 and bit3. I'm calling them "not-use-padding" way and "use-padding" way for short.

In order to explain why the padding part is not used, the book gives the following code:

Concrete2 *pc2; 
Concrete1 *pc1_1, *pc2_2; 
pc1_1 = pc2;
*pc1_1 = *pc2_2; // pc1_1 may point to a Concrete2 or Concrete3 object. 

In "not-use-padding" way, the Concrete1 object pointed to by pc2_2 will be copied to the Concrete1 subobject of the Concrete2/Concrete3 object pointed to by pc1_1. The book also says it's a "memberwise" copy, but it looks more like an "object" copy because it implies the padding part is also copied.

In "use-padding" way, the book says it will override the bit2 member because the corresponding byte of *pc2_2 is a padding byte. Again, I tried it with gcc 4.4.7, it turned out the bit2 member is not overridden. So I'm guessing the copy is a real "memberwise" copy, just val and bit1 are copied.

So my questions are: 1. Am I correct about the gcc activities: "use-padding" way and real "memberwise" copy? 2. The private members of a base class can't be accessed in derived class, but derived class object contains all the private members in its base class subobject (val and bit1) in object model. Why it is designed to contain base class private members even if they can not even be accessed in derived class object? Just for copy operations like *pc1_1 = *pc2_2; ?

Thanks

password636
  • 981
  • 1
  • 7
  • 18
  • #2: Commonly, a class would have public member functions, that in turn use private data members. Even if a derived class cannot access its base class' private members, it can certainly call its public methods. – Igor Tandetnik Feb 19 '16 at 04:45
  • You want to read the C++ Itanium ABI, which describes (part of) the layout used by gcc. In particular, it shows that if you replace `private:` with `public:` in Concrete1, the size of the derived classes changes. – Marc Glisse Feb 19 '16 at 08:44

1 Answers1

0

About your first question:

I tried to reproduce the behaviour you described as "use-padding" at ideone.com with different compilers but failed. Clang, gcc 4.3 and gcc 5.1 seemed to not use the padding of the base class. But from the answers to this Question one can read that gcc indeed makes use of this padding.

The Standard introduces the concept of POD-types, but it states explicitly

9 Classes [class]
(...)
7 A class S is a standard-layout class if it: (...)
(7.6) has all non-static data members and bit-fields in the class and its base classes first declared in the same class (...)

So this does not apply to your class, since you have data members in both base and derived classes. I couldn't find any references about how non-POD-types need to be layouted. One of the answers in the linked question states, that this has been subject of change.

What is important about inheritance is, that - using pointers or references - you can use a derived object as if it were an object of the base class. That means you can use a Concrete3* just as if it were a Concrete1*. But this is independent wheter you use the padding or not, as long as you don't change the layout of Concrete1 when adding the data members of Concrete2 and Concrete3.


About your second question:

The standard states this:

11 Member access control [class.access]
1 A member of a class can be

  • (1.1) private; that is, its name can be used only by members and friends of the class in which it is declared.
  • (1.2) protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see 11.4).
  • (1.3) public; that is, its name can be used anywhere without access restriction.

As you can see, it only specifies where the name can be used. E.g. you can still return references or pointers to private members. This implies that they have to be present in the memory somewhere. In fact the state of an object is defined by the state of its members - private or public is just a matter of where in your code you are allowed to directly access them.

Community
  • 1
  • 1
Anedar
  • 4,235
  • 1
  • 23
  • 41