vptr的初始化语义学

本文深入探讨了多继承环境下vptr的初始化语义学,通过三个修改方案分析了构造函数中成员函数的调用顺序。总结指出vptr初始化发生在基类构造之后,成员初始化之前,以确保构造过程的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、多继承下的调用研究

根据上述的模型当中, 我们可以构建出其构造函数的调用顺序

Point(x, y);
Point3d(x, y, z);
Vertex(x, y, z);
Vertex3d(x, y, z);

源代码:

class Point
{
public:
	Point(float x = 0.0, float y = 0.0) :_x(x), _y(y){ size(); };
	Point(const Point&);
	Point& operator=(const Point&);
	virtual~Point(){}
	virtual void size(){ print(); cout << "Point size: " << sizeof(Point) << endl; }
	virtual void print(){ cout << "This is Point. "; }
protected:
	float _x, _y;
};

class Point3d :virtual public Point
{
public:
	Point3d(float x = 0.0, float y = 0.0, float z = 0.0)
		:Point(x, y), _z(z){
		size();
	}
	Point3d(const Point3d& rhs)
		:Point(rhs), _z(rhs._z){}
	virtual~Point3d(){}
	Point3d& operator=(const Point3d&);
	
	virtual void size(){ print(); cout << "Point3d size: " << sizeof(Point3d) << endl; }
	void print(){ cout << "This is Point3d. "; }
protected:
	float _z;
};

class Vertex :public virtual Point
{
public:
	Vertex(float x = 0.0, float y = 0.0, float t = 0.0)
		:Point(x, y), _t(t){
		size();
	}
	Vertex(const Vertex& rhs)
		:Point(rhs), _t(rhs._t){}
	virtual~Vertex(){}
	Vertex& operator=(const Vertex&);
	virtual void size(){ print(); cout << "Vertex size: " << sizeof(Vertex) << endl; }
	void print(){ cout << "This is Vertex. "; }
protected:
	float _t;

};

class Vertex3d :public Point3d, public Vertex
{
public:
	Vertex3d(float x = 0.0, float y = 0.0, float z = 0.0)
		:Point3d(x, y, z), Vertex(x, y, z){
		size();
	}
	Vertex3d(const Vertex3d& rhs)
		:Point3d(rhs), Vertex(rhs){}
	virtual~Vertex3d(){}
	Vertex3d& operator=(const Vertex&);
	virtual void size(){ print(); cout << "Vertex3d size: " << sizeof(Vertex3d) << endl; }
	void print(){ cout << "This is Vertex3d. "; }
};
运行结果如下:

结果分析:

从结果中可以得出结论, 在构造函数中调用成员函数, 无论是直接调用还是虚化后在调用, 都取决于他正在构造的对象, 与其他任何关系无关。


修改方案一:

Vertex3d(float x = 0.0, float y = 0.0, float z = 0.0)
		:Point3d(x, y, z), Vertex(x, y, z){
		Point*p = new Point; 
		p->print(); size();
	}

运行结果:

结果分析:

证明了其调用词序词序是根据构造模型构造顺序调用的


修改方案二:

Vertex3d(float x = 0.0, float y = 0.0, float z = 0.0)
		:Point3d(x, y, z), Vertex(x, y, z){

		Point*p = new Point3d; p->print(); size();
	}

结果分析:

在构造函数当中, 子类对象的调用机制


修改方案三:

Vertex3d(float x = 0.0, float y = 0.0, float z = 0.0)
		:Point3d(x, y, z), Vertex(x, y, z){
		Point*p = new Vertex3d; p->print(); size();
	}
在该代码当中如果运行, 因为构造函数的调用机制, 将会产生死循环


二、总结

1、vptr的初始化总是在基类构造函数调用操作之后, 在程序员提供的代码调用之前或者是初始化列表所列的成员初始化操作之前。

2、初始化列表所列的成员初始化操作之前设置vptr, 是为了防止以免有虚函数在构造函数中调用出错。

3、 在构造函数中调用成员函数, 无论是直接调用还是虚化后在调用, 都取决于他正在构造的对象, 与其他任何关系无关。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值