有关__declspec(property)的多态性

MSVC为了方便C++编写COM客户程序引入了property关键字。可能有些人不知道VC有这个功能,但是property的概念相信大家都清楚。例如:

class A
{
public:
	int _width;
	int get_width()
	{
		return _width;
	}
	void set_width(int value)
	{
		_width = value;
	}
	__declspec(property(get=get_width,put=set_width)) int width;
}

int main()
{
	A a;
	a.width = 3;
	int w = a.width;
	return 0;
}

这样的代码大家都不会陌生,可以大大方便我们的使用。

但是有个问题。虚函数与多态。假如写出下面的代码:

class A
{
public:
	virtual int get_width()
	{
		return 1;
	}
	__declspec(property(get=get_width)) int width;
}

class AA : public A
{
public:
	virtual int get_width()
	{
		return 2;
	}
}

那么编译器会如何操作呢。经过试验,发现在这个问题上VC还是有一些BUG的。具体结果如下:

1、A a; int w = a.width;

这样当然调用的是A::get_width,没有任何问题。

2、A *a = new A(); int w = a->width;

这样调用的也是A::get_width,正确。

3、AA aa; int w = aa.width;

注意了,这次调用的还是A::get_width,错误。

4、A *a = new AA(); int w = a->width;

这样调用的是AA::get_width,正确。

也就是说,编译器仅将property绑定到基类的操作方法上,而并没有识别派生类中有没有重写这个方法,所以使用值类型的对象调用时产生的还是前期绑定汇编,调用call被硬编码到基类中的函数上。而如果通过指针调用,编译器产生的代码为通过虚函数表调用的后期绑定汇编代码,由于后期绑定的特性,自动导向了派生类中的函数。即VC的property本身并不支持多态。事实上,COM函数都是以指针方式调用的,也就是说在property关键字原本用到的地方是不可能写出值类型的成员变量寻址的。

认识到这样的情况,如果要编写此类程序(一般为类似与VB与.NET的窗口应用,如对窗口的Text赋值),可以考虑人为制定一些编码规范,如将class仅作为指针类型使用,将struct作为数据结构的值类型使用。显然struct仅需要构造函数而无需继承。

Leave a Reply

Your email address will not be published. Required fields are marked *