84 C++对象模型探索。数据语义学 - 继承多个类的时的数据布局问题。

此章节分析多继承问题,难点,但是非重点,实际开发中,多继承用的很少,容易被code review,可以不看。

我们要访问一个类对象中的成员
成员的定位是通过如下两个因素决定的:this指针(编译器会自动调整) ,以及该成员的偏移量,
这样this指针的调整,都需要编译器的介入来完成。

Teacher  继承 Teacher1,继承Teacher2,继承Teacher3

84 C++对象模型探索。数据语义学 - 继承多个类的时的数据布局问题。_第1张图片

//多继承下的成员布局分析

class Teacher13base1 {
public:
	Teacher13base1() {
		cout << "Teacher13base1 gouzao this = " << this << endl;
	}
public:
	int base1age;

private:
	int base1privateage;
public:
	virtual void base1virtualfunc() {

	}
};

class Teacher13base2 {
public:
	Teacher13base2() {
		cout << "Teacher13base2 gouzao this = " << this << endl;
	}
public:
	int base2age;

private:
	int base2privateage;
public:
	virtual void base2virtualfunc() {

	}
};

class Teacher13base3 {
public:
	Teacher13base3() {
		cout << "Teacher13base3 gouzao this = " << this << endl;
	}
public:
	int base3age;

private:
	int base3privateage;
public:
	virtual void base3virtualfunc() {

	}
};

class Teacher13 :public Teacher13base1, public Teacher13base2, public Teacher13base3{
public:
	Teacher13() {
		cout << "Teacher13 gouzao this = " << this << endl;
	}
public:
	int age;

private:
	int privateage;
public:
	virtual void virtualfunc() {

	}
};

void main() {

	cout << sizeof(Teacher13base1) << endl;//12  Teacher13base1的vptr + 2ge int
	cout << sizeof(Teacher13base2) << endl;//12  Teacher13base2的vptr + 2ge int
	cout << sizeof(Teacher13base3) << endl;//12  Teacher13base3的vptr + 2ge int
	cout << sizeof(Teacher13) << endl;//44    8个 int = 32 + 3个vptr(Teacher13base1的vptr(Teacher13的vptr	) + Teacher13base2的vptr + Teacher13base3的vptr )

	printf("Teacher13::base1age的地址是%p\n", &Teacher13::base1age);
	printf("Teacher13::base2age的地址是%p\n", &Teacher13::base2age);
	printf("Teacher13::base3age的地址是%p\n", &Teacher13::base3age);
	printf("Teacher13::base1age的地址是%p\n", &Teacher13::age);
	Teacher13 tea;
	tea.base1age = 1;
	tea.base2age = 2;
	tea.base3age = 3;
	tea.age = 4;

	cout << "断点在这里" << endl;

}


12
12
12
44
Teacher13::base1age的地址是00000004
Teacher13::base2age的地址是00000004
Teacher13::base3age的地址是00000004
Teacher13::base1age的地址是00000024
Teacher13base1 gouzao this = 012FFB10
Teacher13base2 gouzao this = 012FFB1C
Teacher13base3 gouzao this = 012FFB28
Teacher13 gouzao this = 012FFB10
断点在这里

分析

0x012FFB10  f4 3e 9c 00 01 00 00 00 cc cc cc cc 04 3f 9c 00 02 00 00 00 cc cc cc cc 10 3f 9c 00  
0x012FFB2C  03 00 00 00 cc cc cc cc 04 00 00 00 cc cc cc cc

f4 3e 9c 00  Teacher13base1 的vptr指针的地址,也是Teacher13的vptr指针的地址

01 00 00 00  Teacher13base1.base1age

cc cc cc cc  Teacher13base1.base1privateage
 
04 3f 9c 00  Teacher13base2 的vptr指针的地址
 
02 00 00 00  Teacher13base2.base2age
 
cc cc cc cc  Teacher13base2.base2privateage
  
10 3f 9c 00  Teacher13base3 的vptr指针的地址
  
03 00 00 00  Teacher13base3.base3age
  
cc cc cc cc  Teacher13base3.base3privateage

04 00 00 00  teacher13.age

cc cc cc cc  teacher13.privateage

84 C++对象模型探索。数据语义学 - 继承多个类的时的数据布局问题。_第2张图片

继承多个类的时候,最终子类和第一个继承的父类公用 vptr,this指针指向相同

其他父类的this指针都和tea不一样

其他父类的vptr都在原来的位置上。

那么根据上述的结论,如下的代码会怎么样呢?


	Teacher13 tea13;

	cout << "tea13的地址为:" << &tea13 << endl;

	Teacher13base2 *pbase2 = &tea13;
	cout << "pbase2的地址为:" << pbase2 << endl;

tea13的地址为:006BFB3C
pbase2的地址为:006BFB48

这个结论我们已经知道了,Teacher13base2 的 this指针会想下移动12字节,因此这两值不一样。

从编译器的角度分析,那么一定是在Teacher13base2 *pbase2 = &tea13这一行做了额外的事情。

//Teacher13base2 *pbase2 = (Teacher13base2*)(  ((char*)&tea13) + sizeof(Teacher13base1));

放开这一行测试结果如下:

tea13的地址为:00CFF82C
pbase2的地址为:00CFF838

总结上面的例子:

	Teacher13 tea13;

	cout << "tea13的地址为:" << &tea13 << endl;

	Teacher13base2 *pbase2 = &tea13;
	cout << "pbase2的地址为:" << pbase2 << endl;
	//站在编译器角度,是这么干的
	Teacher13base2 *pbase3 = (Teacher13base2*)(((char*)&tea13) + sizeof(Teacher13base1));
	cout << "pbase2的地址为:" << pbase3 << endl;
}

tea13的地址为:012FFA10
pbase2的地址为:012FFA1C
pbase2的地址为:012FFA1C

    //再来看一个特殊的

	//再来看一个特殊的

	Teacher13base2 *pbase4 = new Teacher13();//这句话的理解是:new Teacher13肯定是创建了44个字节的数据。但是this指针是Teacher13base2* 的,因此this指针是指向 内存模型的中间的位置
	//delete pbase4;//那么这样delete,相当于在内存模型的中间位置delete,会有runtime exception

	//那么要怎么删除呢?再转化成 teacher13 *,然后再delete

	Teacher13 * temppbase4 = (Teacher13 *)pbase4;
	delete temppbase4;

	//实际上编译器内部是这么干的
	//Teacher13 * temppbase5 = (Teacher13 *)((char *)pbase4 -sizeof(Teacher13base1));
	//delete temppbase5;

上述一些特殊的case,都是因为多继承引起的,多继承的时候,使用了非第一个继承的指针,指向子类对象,由于new 出来的是整个子类对象,但是this指针因为调整的问题,确不在最 子类对象的最上面。

建议少用多继承。

你可能感兴趣的:(c++,算法,开发语言)