t x1, int y1);
+ virtual void drawLine(double x0, double y0, double x1, double y1);
virtual void drawLine(Point p0, Point p1);
virtual void drawRectangle(int x0, int y0, int x1, int y1);
+ virtual void drawRectangle(double x0, double y0, double x1, double y1);
virtual void drawRectangle(Point p0, Point p1);
virtual void drawArc(int x, int y, int r);
+ virtual void drawArc(double x, double y, double r);
virtual void drawArc(Point p, int r);
};
受 C++ 二进制兼容性方面的限制,我们不能这么做。其本质问题在于 C++ 以 vtable[offset] 方式实现虚函数调用,而 offset 又是根据虚函数声明的位置隐式确定的,这造成了脆弱性。我增加了 drawLine(double x0, double y0, double x1, double y1),造成 vtable 的排列发生了变化,现有的二进制可执行文件无法再用旧的 offset 调用到正确的函数。
怎么办呢?有一种危险且丑陋的做法:把新的虚函数放到 interface 的末尾,例如:
--- old/graphics.h 2011-03-12 13:12:44.000000000 +0800
+++ new/graphics.h 2011-03-12 13:58:22.000000000 +0800
@@ -7,11 +7,15 @@
class Graphics
{
virtual void drawLine(int x0, int y0, int x1, int y1);
virtual void drawLine(Point p0, Point p1);
virtual void drawRectangle(int x0, int y0, int x1, int y1);
virtual void drawRectangle(Point p0, Point p1);
virtual void drawArc(int x, int y, int r);
virtual void drawArc(Point p, int r);
+
+ virtual void drawLine(double x0, double y0, double x1, double y1);
+ virtual void drawRectangle(double x0, double y0, double x1, double y1);
+ virtual void drawArc(double x, double y, double r);
};
这么做很丑陋,因为新的 drawLine(double x0, double y0, double x1, double y1) 函数没有和原来的 drawLine() 函数呆在一起,造成阅读上的不便。这么做同时很危险,因为 Graphics 如果被继承,那么新增虚函数会改变派生类中的 vtable offset 变化,同样不是二进制兼容的。
另外有两种似乎安全的做法,这也是 COM 采用的办法:
1. 通过链式继承来扩展现有 interface,例如从 Graphics 派生出 Graphics2
--- graphics.h 2011-03-12 13:12:44.000000000 +0800
+++ graphics2.h 2011-03-12 13:58:35.000000000 +0800