一、考察下面的一个例子
//某个foo.h头文件,有如下声明
extern float x;
//在Point3d.h文件中
#include "foo.h"
class Point3d
{
public:
Point3d(float, float, float);
//问题:这里返回的是哪一个x哪?
float X()const { return x; }
void X(float new_X) { x = new_X; }
//...
private:
float x, y, z;
};
问题思考
- 在上面的Point3d.h头文件中,第一个X()成员函数会返回什么哪?class外部的(x)哪?还是class内部的数据成员x哪?
问题解析
- 在今天看来,第一个X()成员函数时内部的x数据成员
二、早期C++标准的数据成员绑定
- 为了防止类内数据成员与全局数据成员产生冲突,早期C++设置了下面两种防御性程序设计风格
风格①
- 把所有的数据成员放在class声明起头处,以确保正确的绑定
#include "foo.h"
class Point3d
{
float x, y, z;
public:
//...
float X()const { return x; }
//...
};
风格②
- 把所有的内联函数,不管大小都放在class声明之外
#include "foo.h"
class Point3d
{
float x, y, z;
public:
//...
float X()const;
//...
};
inline float Point3d::X()const
{
return x;
}
- 总结:上面这种设计风格到今天还存在,虽然在C++2.0之后这种必要性就消失了。这些古老的语言规则被称为“member rewriting rule”,大意是“一个inline函数,在整个class声明未被完全看见之前,是不会被评估求值的”
三、现代C++标准的数据成员绑定规则
- C++标准精炼上面的“rewriting rule”,定义了如下的规则:
- 对数据成员的分析,只有在整个class的声明都出现了才开始(直到出现classs右大花括号“}”)
演示说明
extern int x;
class Point3d
{
public:
//...
//该函数本身的分析不会立即执行,而是等到class声明的右大括号“}”出现才开始
float X()const { return x; }
//...
private:
float x, y, z;
};
//在此,class的分析才开始,所以X()成员函数分析的是自身类的x数据成员,而不是全局的x
四、参数列表分析
- 通过下面这些演示案例,为了说明本文所介绍的“Data Member的绑定”是与数据成员有关的规则,而其他规则则不可以使用,需要具体分析
- 参数列表的规则与上面的数据成员分析规则有一些不一样(请看下面的演示案例)
演示案例①
- 尽管类型也使用typedef进行了类型声明,但是两个mumble成员函数使用的都是全局的typedef声明
typedef int length;//全局
class Point3d
{
public:
void mumble(length val) { _val = val; }
length mumble() { return _val; }
private:
typedef float length; //局部
length _val;
};
演示案例②
- 为了演示上面那个演示案例的正确性,我们将全局typedef注释掉,程序会报错
//typedef int length;
class Point3d
{
public:
void mumble(length val) { _val = val; }
length mumble() { return _val; }
private:
typedef float length; //局部
length _val;
};
![](https://img-blog.csdnimg.cn/20200113101853992.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDUzMjg1,size_16,color_FFFFFF,t_70)
演示案例③
- 为了正确使用类内的typedef声明,我们应该将类内typedef放置在class声明的最前面
- 下面的程序是语义正确的
typedef int length; //全局
class Point3d
{
private:
typedef float length; //局部
length _val;
public:
void mumble(length val) { _val = val; }
length mumble() { return _val;
};