一:在类外部定义成员函数
当我们在类外部定义成员函数时,一旦遇到类名,定义的剩余部分(即参数列表与函数体)就在类的作用于之内了;相反,在遇到函数名之前的部分(返回值)位于类的作用于之外。
#include <vector>
class StudentManager;
class Student;
class Student{
private:
friend class StudentManager;
};
class StudentManager{
public:
using StudentId = std::vector<Student>::size_type; // 每个学生的编号
void clear(StudentId);
StudentId append(const Student);
private:
std::vector<Student> students;
};
void StudentManager::clear(StudentId i) {
students.erase(students.begin()+i);
}
StudentManager::StudentId StudentManager::append(const Student s) {
students.push_back(s);
}
我们定义了一个Student类,并用StudentManager来管理Student类。我们在StudentManager类外部定义了StudentMnager类的两个成员函数clear与append,用于清除指定位置的元素与添加新的Student成员,StudentId为StudentManager类的成员变量。
在定义clear函数时,由于函数的返回类型不属于StudentManager类,所以未加作用域运算符,在遇到函数名后使用作用域运算符进入StudentManager作用域,而后的参数列表与函数体默认是位于StudentManager类中的,所以没有出现作用域运算符。
但在定义append函数时,由于函数的返回类型是StudentManager类的成员且未遇到函数名,则该处位于StudentManager作用域外,需显式添加作用域运算符来使用类中成员。
二:类中的名字查找
对于定义在类中的成员函数来说,解析其中名字时首先编译成员的声明,等到类全部可见后再编译函数体。
1.用于类成员声明的名字查找
声明中使用的名字,包括返回类型与参数列表中使用的名字,必须在使用前可见。如果某个名字在类中未出现,则会继续在定义该类的作用域中继续查找。
typedef double Money;
int bal;
class Account{
public:
Money balance(){return bal;}
private:
Money bal;
};
当编译器看到balance函数声明语句时 ,它将在Account类的范围内寻找对Money的声明。编译器只会考虑出现balance函数前出现的声明(因为未到编译函数体阶段)由于在类内未找到,则在定义Account类的作用域内查找,发现其为double类型的别名,则double被用于balance函数的返回类型。
其次,balance函数体在整个类全部可见后被处理,因此该return语句返回的是类中定义的Money类型的bal,而非函数体之外的int类型的bal。
2.类型名的特殊处理
一般来说,内层作用域可以重新定义外层作用域中的名字,即使改名字已经在内层作用域中使用过。然而在类中,如果成员使用了外层作用域中的某个名字,且恰好该名字代表一种类型,则类不能在之后重新定义该名字。
typedef double Money;
class Account{
public:
Money balance(){
return bal;
}
private:
typedef double Money; // error!不能重新定义Money
Money bal;
};
即使重定义的Money类型与外层作用域中的类型一致,该代码依然是错误的。
编译器不负责检查此类错误!
3.成员函数定义中的名字查找
成员函数中使用的名字解析方法:首先只考虑在成员函数中出现的声明;如果未找到则继续在类内查找,此时类中的所有成员均可考虑;最后在类定义之前的作用域中查找。
int a = 2;
class Class{
public:
int get(int b){
return a * b * c;
}
private:
int b = 3;
int c = 4;
};
编译器在get函数中遇到了 a,b,c名字。首先在函数作用域内查找相关名字,在参数列表中找到了b,则函数体中的b为参数列表中的b;在函数作用域内未找到a与c,进一步在类中查找,找到了名字c,则函数体中的c为类中定义的c = 4;a未在类中找到,进一步在类定义的作用域中找到了a = 2,则函数体中的a为2。如果我们调用该函数并将b赋值为5,则其返回值为40。
当然我们可以使用作用域运算符来将b赋值为类中private部分定义的b=3,此时无论参数b为何值,其返回值都为24。