Data Member 的绑定
考察以下代码:
extern float _x; //user code class Point3d { public: Point3d(float, float, float); //问题来了, 是哪一个 _x? float X() const {return _x;} void X(float x) const { _x = x;} private: float _x, _y, _z; };
许多人可能会说, 应该是 class 中的 _x,但是很难保证在不同的编译器上行为一致, 因此就有了以下两种防御性的设计风格:
class Point3d { //#1 //在 class 声明的起头处放置所有的 data member float x, y, z; public: float X() const {return _x;} //... }; class Point3d { //#2 //把 inline 移到 class 之外 Point3d(); float X() const; void X(float) const; //... }; inline float Point3d:: X() const {return _x;}
这种风格至今存在, 但已不是必要手段, C++ 2.0 之后, 有了这样一个规则:如果一个 inline 函数实体在整个 class 声明未被看见之前, 是不会被评估求值的。也就是说, 考察以下代码:
extern int _x; class Point3d { public: //对于函数本身的分析将延迟 //直到 class 声明的右大括号出现才开始 float X() const {return _x;} private: float _x; }; //事实上, 对 class 的分析从这才刚开始
因此,在一个 inline member function 躯体之内的一个 data member 绑定操作, 会在整个 class 声明后才发生。
但是, 这对于 member function 的 argument list 并不为真。Argument list 中的名称还是会在它们第一次遭遇时被适当地 resolve。 因此在 extern 和 nested type names 之间的非直觉绑定还是会发生, 考察以下代码:
typedef int length; class Point3d { public: //wtf!length 被 resolve 为 global //没问题, _val 被 resolve 为 Pointd::_val void mumble(length val) {_val = val;} //此处在接下来 定义 length 后会被认定不合法 length mumble() {return _val;} //... private: //length 必须在此 class 对它的第一个参考操作之前被发现 //这样就导致编译器显示先前的参考操作不合法 typedef float length; length _val; };
在这样的情况下, 就需要之前的防御风格: 把 nwsted type 声明放在 class 的起始处, 就可以避免导致的非直觉绑定。