类(四):类的作用域

<基于 C++ Primer P253>
在类的作用域之外,普通的数据和函数成员只能由对象、引用或者指针使用成员访问运算符来访问。对于类类型成员则是哟个作用域运算符访问。

Screen::pos ht = 24, wd = 80; // 使用 Screen 定义的 pos 类型
Screen scr(ht, wd, ' ');
Screen *p = &scr;
char c = scr.get();
c = p->get();

(1)作用域和定义在类外部的成员
一个类就是一个作用域,因此当我们在类的外部定义成员函数时必须同时提供类名和函数名,因为在类的外部成员的名字被隐藏起来了。
而一旦遇到了类名,定义的剩余部分(参数列表和函数体)就在类的作用域之内了。因此,我们可以直接使用类的其他成员而无须再次授权了。

void Window_mgr::clear(ScreenIndex i)
{
	Screen &s = screen[i];
	s.contents = string(s.height * s.width, ' ');
}

同时,我们注意到函数声明中函数的返回类型位于函数名之前。因此当成员函数定义在类的外部时,返回类型中使用的名字都位于类的作用域之外。这是,返回类型必须指明它是哪个类的成员。

class Window_mgr
{
public:
    // 向窗口添加一个 Screen,返回它的编号
	ScreenIndex addScreen(const Screen&);
};
Window_mgr::ScreenIndex
Window_mgr::addScreen(const Screen &s)
{
	screen.push_back(s);
	return screen.size() - 1;
}

1.名字查找与类的作用域
名字查找是指寻找与所用名字最匹配的声明的过程,其一般流程为:
①首先,在名字所处的块中寻找其声明语句,只考虑在名字的使用之前出现的声明。
②如果没有找到,继续查找外层作用域。
③如果最终没有找到匹配的声明,则程序报错。
对于定义在类内部的成员函数来说,解析其中名字的方式有所不同。因为类的定义分两部处理:
①首先编译成员的声明。
②直到类全部可见后才编译函数体。
因此,成员函数可以使用类中定义的任何名字。
1.用于类成员声明的名字查找
两阶段的处理方式只适用于成员函数的函数体中使用的名字。
声明中使用的名字,包括返回类型或者参数列表中使用的名字,都必须在使用前确保可见。

typedef double Money;
string bal;
class Account
{
public:
	Money balance() {return bal;} // Money 在外层作用域中定义,bal 在整个类可见后处理
private:
	Money bal;
};

2.类型名要特殊处理
一般来说,内层作用域可以重新定义外层作用域中的名字,即使该名字已经在内层作用域中使用过。但是在类中,如果成员函数使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字。

typedef double Money;
class Account
{
public:
	Money balabce() {return bal;}
private:
	typedef double Money; // 错误:不能重复定义 Money,即使与外层作用域一致也不行
	Money bal;
	// ...
};

类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类型的定义之后。
3.成员定义中的普通块作用域的名字查找
成员函数中使用的名字按照如下方式解析:
①首先在成员函数内部查找该名字的声明,只有在函数使用之前出现的声明才被考虑。
②如果没有找到,在类内继续查找,这是类的所有成员都可以被考虑。
③如果没有找到,在成员函数定义之前的作用域内继续查找。

// 注意:这段代码仅为了说明而用,不是一段很好的代码
// 通常情况下不建议为参数和成员使用同样的名字
int height;                        // 外层作用域的 height
class Screen
{
public:
	typedef std::string::size_type pos;
	void dummy_fun(pos height)
		{cursor = width * height;} // 这里的 height 指的是参数声明
private:
	pos cursor = 0;
	pos height = 0, width = 0;
}; 

上面的代码中,height 参数隐藏了同名的成员。如果想绕开查找规则,可以将代码改为:

void Screen::dummy_fcn(pos height)
{
	cursor = width * this->height; // 成员 height
	// 等价的写法:cursor = width * Screen::height; 
}

最好的方式是给参数起另外一个名字:

void Screen::dummy_fcn(pos ht)
{
	cursor = width * height; // 成员 height
}

4.类作用域之后,在外围的作用域中查找
上面的代码中,height 参数隐藏了同名的外层作用域的成员。如果想绕开查找规则,可以将代码改为:

void Screen::dummy_fcn(pos height)
{
	cursor = width * ::height; // 全局的 height 
}

5.在文件中名字的出现处对其进行解析
当成员定义在类的外部时,名字查找的第三步不仅要考虑类定义之前的全局作用域中的声明,还需要考虑在成员函数定义之前的全局作用域中的声明。

int height;                        // 外层作用域的 height
class Screen
{
public:
	typedef std::string::size_type pos;
	void setHeight(pos)
	pos height = 0;
};
Screen::pos verify(Screen::pos); // 全局函数 verify 的声明
void Screen::setHeight(pos var)
{
	height = verify(var); // height:类的成员;verify:全局函数;var:参数;
}

注意,虽然全局函数 verify 的声明在 Screen 类的定义之前是不可见的,但是名字查找的第三步考虑在成员函数定义之前的全局作用域中的声明,因此是正确的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值