第二章 数据成员模型
1.1. 继承与数据成员
考察下面的例子:
0001 class Concrete1
0002 {
0003 public :
0004 int val;
0005 char bit1;
0006 };
0007 class Concrete2 : public Concrete1
0008 {
0009 public :
0010 char bit2;
0011 };
0012 class Concrete3 : public Concrete2
0013 {
0014 public :
0015 char bit3;
0016 };
类继承关系如下:
Concrete1
Concrete2
Concrete3
正确的对象内存布局
Concrete1 Object Concrete2 Object Concrete3 Object
4
int val
1
char bit1
3
Padding 3 bytes
8
Concrete1
1
char bit2
3
Padding 3 bytes
12
Concrete2
1
char bit3
3
Padding 3 bytes
验证对象
0001 #include <iostream>
0002 //-------------------------------------------------------------------
0003 class Concrete1
0004 {
0005 public :
0006 int val;
0007 char bit1;
0008 };
0009 class Concrete2 : public Concrete1
0010 {
0011 public :
0012 char bit2;
0013 };
0014 class Concrete3 : public Concrete2
0015 {
0016 public :
0017 char bit3;
0018 };
0019 //-------------------------------------------------------------------
0020 int main()
0021 {
0022 cout << sizeof(Concrete1) << endl;
0023 cout << sizeof(Concrete2) << endl;
0024 cout << sizeof(Concrete3) << endl;
0025 int i;
0026 cin >> i;
0027 }
输出结果为:
8
12
16
从以上模型结构可以看出,子类继承了父类的数据,同时也继承了父类因补齐原则而引入的“多余的”字节。为什么在子类继承的同时不去除父类因补齐原则而引入的“多余的”字节呢?为什么不将模型转换成如下形式,以便更加节约内存,减少因类继承关系而造成的对象内存空间的浪费呢?
Concrete1 Object Concrete2 Object Concrete3 Object
4
int val
1
char bit1
3
Padding 3 bytes
4
int val
1
char bit1
1
char bit2
2
Padding 2 bytes
4
int val
1
char bit1
1
char bit2
1
char bit3
1
Padding 1 bytes
如此一来,三个对象的内存占用大小都变成8个字节,因而在创建多个派生类对象的时候,将更节约内存。
之所以不采用这样的模型,是因为该模型会在复制对象的时候产生不可预期的错误,例如:
0001 Concrete2 *pc2;
0002 Concrete1 *pc1;
0003 pc2 = new Concrete2;
0004 pc1 = new Concrete1;
0005 *pc2 = *pc1;
上述模型模型将在第五行产生不可预期的错误,这是因为复制Concrete1对象的时候会破坏Concrete2对象的数据成员。
bit2将被指定一个不确定的值,破环了原来的数据内容,这种行为不是所期望的。
Concrete1 Object Concrete2 Object
4
int val
1
char bit1
3
Padding 3 bytes
4
int val
1
char bit1
1
char bit2
2
Padding 2 bytes
Copy
同理,Concrete1 ---> Concrete2,Concrete2 -à Concrete3也都会破坏数据成员。