94 C++对象模型探索。对象构造语义学 - 继承体系下的对象构造步骤

一.对象的构造顺序

二.虚函数的继续观察

这个结论是早都知道的 : 父类的构造函数--->子类的构造函数调用

这里使用三个类 在vs2017中观察,构造顺序,这里主要是学习方法,有了方法后,在遇见后面复杂的case下可以自己debug

//通过反汇编观察
class Teacher28grandpa {
public:
	Teacher28grandpa() {
		cout << "Teacher28grandpa 构造方法被调用" << endl;
	}
	~Teacher28grandpa() {
		cout << "Teacher28grandpa xigou 方法被调用" << endl;
	}

	virtual void virfunc() {
		cout << "Teacher28grandpa virfunc 方法被调用" << endl;
	}
};

class Teacher28father :public Teacher28grandpa {
public:
	Teacher28father() {
		cout << "Teacher28father 构造方法被调用" << endl;
	}

	~Teacher28father() {
		cout << "Teacher28father xigou 方法被调用" << endl;
	}

	virtual void virfunc() {
		cout << "Teacher28father virfunc 方法被调用" << endl;
	}
};

class Teacher28 :public Teacher28father {
public:
	//这里还使用了 初始化成员列表,顺便观察一下 mage的值是什么时候
	Teacher28(int age):mage(age) {
		cout << "Teacher28 构造方法被调用" << endl;
	}

	~Teacher28() {
		cout << "Teacher28 xigou 方法被调用" << endl;
	}
	//这里有虚函数,目的是观察,虚函数在构造方法中是如何构造的。
	virtual void virfunc() {
		cout << "Teacher28 virfunc 方法被调用" << endl;
	}

	int mage;
};

void main() {
	Teacher28grandpa *ptea = new Teacher28(888);
	ptea->virfunc();
}

结果:

Teacher28grandpa *ptea = new Teacher28(888);
    00DFFD82  call        Teacher28::Teacher28 (0DF1429h) //内部调用
				00DF68FF  call        Teacher28father::Teacher28father (0DF135Ch) //内部调用
						00DF69FF  call        Teacher28grandpa::Teacher28grandpa (0DF1735h) //内部调用
								
								
								00DF6AD0  mov         dword ptr [eax],offset Teacher28grandpa::`vftable' (0E08E64h)  //虚函数指针的赋值
								cout << "Teacher28grandpa 构造方法被调用" << endl;
						
						00DF6A0E  mov         dword ptr [eax],offset Teacher28father::`vftable' (0E08EECh)  //虚函数指针的赋值
						cout << "Teacher28father 构造方法被调用" << endl;


				00DF690E  mov         dword ptr [eax],offset Teacher28::`vftable' (0E08F74h) //虚函数指针的赋值
				00DF6917  mov         ecx,dword ptr [age]  //这是初始化列表赋值的时机
				cout << "Teacher28 构造方法被调用" << endl;

结论:


Teacher28grandpa *ptea = new Teacher28(888);
    00DFFD82  call        Teacher28::Teacher28 (0DF1429h) //内部调用
                00DF68FF  call        Teacher28father::Teacher28father (0DF135Ch) //内部调用
                        00DF69FF  call        Teacher28grandpa::Teacher28grandpa (0DF1735h) //内部调用
                                
                                
                                00DF6AD0  mov         dword ptr [eax],offset Teacher28grandpa::`vftable' (0E08E64h)  //虚函数指针的赋值   1
                                cout << "Teacher28grandpa 构造方法被调用" << endl;  2
                        
                        00DF6A0E  mov         dword ptr [eax],offset Teacher28father::`vftable' (0E08EECh)  //虚函数指针的赋值   3
                        cout << "Teacher28father 构造方法被调用" << endl;   4


                00DF690E  mov         dword ptr [eax],offset Teacher28::`vftable' (0E08F74h) //虚函数指针的赋值   5
                00DF6917  mov         ecx,dword ptr [age]  //这是初始化列表赋值的时机   6
                cout << "Teacher28 构造方法被调用" << endl;   7

三.构造函数中对虚函数的调用

结论:

构造函数中调用虚函数不会走虚函数指针。

class Teacher28 :public Teacher28father {
public:
	//这里还使用了 初始化成员列表,顺便观察一下 mage的值是什么时候
	Teacher28(int age):mage(age) {
		cout << "Teacher28 构造方法被调用 start" << endl;
		virfunc();
		cout << "Teacher28 构造方法被调用 end" << endl;
	}

	~Teacher28() {
		cout << "Teacher28 xigou 方法被调用" << endl;
	}
	//这里有虚函数,目的是观察,虚函数在构造方法中是如何构造的。
	virtual void virfunc() {
		cout << "Teacher28 virfunc 方法被调用" << endl;
	}

	int mage;
};

但是从debug来看,这时候虚函数指针已经有值了,但是编译器还是没有走 虚函数指针查找。

这可以理解为:当构造函数还没有完全好的时候,不让走。

debug 构造方法中的 virfunc() 方法,如下:

        virfunc();
00546946  mov         ecx,dword ptr [this]  
00546949  call        Teacher28::virfunc (05411A9h)  
        cout << "Teacher28 构造方法被调用 end" << endl;

奇怪的现象:构造函数中调用虚函数1,然后再虚函数1中调用虚函数2

注意:这里会有不同,调用虚函数1是不会走虚函数指针,但是虚函数2会走虚函数指针。

四.举一反三

这里使用三个类 在vs2017中观察,构造顺序,这里主要是学习方法,有了方法后,在遇见后面复杂的case下可以自己debug

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