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仅需要构造函数而无需继承。