在类的作用域外访问类内普通数据成员和成员函数只能通过对象、引用、或者指针使用成员访问运算符("."和"->")
Q:在类外部定义成员函数为什么要提供类名和函数名?
A:一个类是一个作用域,在类的外部成员的名字呗隐藏了起来。一旦遇到类名,定义的剩余部分就在类的作用域之内了,剩余部分包括(参数列表,函数体)。
例子:
void Window_mgr::clear(ScreenIndex i) {
Screen& s = screens[i];
s.contents = string(s.height * s.width, ' ');
};
编译器在处理参数列表之前,就已经知道我们正处于Window_mgr类之中了。
- 当返回类型属于类内成员时候,必须指明是哪个类定义了的它。
Window_mgr::ScreenIndex Window_mgr::addScreen(const Screen& s) {
screens.push_back(s);
return screens.size() - 1;
}
7.4.1名字查找与类的作用域
名字查找:寻找与所用名字最匹配的声明的过程
- 在名字所在的块中寻找声明语句,只考虑在这个名字使用之前的声明
- 没找到的话,就继续查找外层作用域
- 如果没有找到匹配的声明,则程序报错。
定义在类内部的成员函数来说,解析名字的方式和上述不同。
类的定义分两步:(这种两阶段的处理方式只适用于成员函数中使用的名字)
- 首先,编译成员的声明
- 知道类全部可见后才编译函数体。
NOTE:编译器处理完类中全部声明才会处理成员函数的定义,所以和函数的声明顺序无关。
例子:
typedef double Money;
string bal;
class account {
public:
Money balance() { return ball; }
private:
Money ball;
};
几点需要注意:
- 类中先去编译所有的声明,此时发现没有Money数据类型,就到类外查找。发现了Money就是double类型
- balance函数题在整个类可见后处理,所以此时return的数据类型(ball)是类内的Money类型而不是string类型。
类型名需要特殊处理
一般的,内层作用域可以重新定义外层作用域中的名字。但是,类是很特别的,不允许在类中重新定义外层作用域的某个名字。
typedef double Money;
string bal;
class account {
public:
Money balance() { return ball; }
private:
Money ball;
typedef double Money; //错误,不能重新定义!但是vs没报错!有些编译器可以顺利的通过这样的代码。
//实际上是一种错误行为!
};
成员定义中的普通快作用域的名字查找
成员函数名字查找的顺序:
- 函数体内
- 类内
- 类外
汗。。。这书写的太尼玛的复杂了!
不就是说明:
在类中,函数参数列表当中的变量不要和类中定义的变量采用“相同的名字”,否则会混淆吗?
万一要是用了怎么办呢?
- 如果在类中定义了一个height,参数列表也有一个height,想用类中那个
void Screen::dummy_fcn(pos height){ cursor = width * this->height; //或者 //cursor = width * Screem::height; }
- 如果想用全局变量中的那个。
void Screen::dummy_fcn(pos height){ cursor = width * ::height; }
- 当成员定义在类的外部,不仅仅考虑在类定义之前的全局变量,即使是在成员函数定义之前的都可以使用
int height; class Screen { public: typedef string::size_type pos; void setHeight(pos); pos height = 0; //隐藏了外层的height }; Screen::pos verify(Screen::pos ); void Screen::setHeight(pos var) { height = verify(var); }