c++ 类作用域中的名字查找


一.类成员声明的名字查找

按以下方式确定在类成员的声明中用到的名字

* 检查出现在名字使用之前的类成员声明

* 如果第一不查找不成功,则检查包含类定义的作用域中出现的声明以及出现在类定义之前的声明.

例如:

typedef double Money;
class Account
{
   public:
      Money balance() { return bal;}
    private:
        Money bal;
 };

在处理balance函数的声明时,编译器首先在类Account中查找Money的声明,编译器只考虑出现在Moeny使用之前的声明,因为找不到任何成员声明,编译器随后在全局作用域中查找Money的声明.找到了Money的声明,并将它用作balance()函数的返回类型 和 数据成员bal的类型.

通常,名字必须在使用之前进行定义. 而且,如果一个名字被用作类型名, 该名字就不能重复定义:

typedef double Money;
class Account
{
   public:
      Money balance() { return bal;}
<span style="font-family: Arial, Helvetica, sans-serif;">      typedef long double Money;</span>
        Money bal;
 };
上面的代码回报错,因为重复定义了Money.


二.在类成员定义中的名字查找

按以下方式确定在成员函数的函数体中用到的名字

* 首先检测成员函数局部作用域的声明

*如果在成员函数中找不到该名字的声明, 则检查对所有类成员的声明

*如果在类中找不到该名字的声明,则检查在此成员函数定义之前的作用域中出现的声明

例如:

const int pg=10;
class A
{
   public:
      int  fun(int  pg)
      {
          return pg;  //哪个pg?
      }
   private:
      int  pg;
};

上面的代码中fun()函数内return 的pg是哪个pg呢?  可以测试一下

int main()
{
  int x;
  A test;
  x=test.fun(3);
  
  cout<<x<<endl;
  return 0;
} 



运行结果是三,说明函数fun()返回的是形参声明的那个pg.

查找fun的定义中使用的名字pg时, 编译器首先在该函数的局部作用域中查找. 函数局部作用域中声明了一个函数形参.查找成功, 形参pg会屏蔽名为pg的成员.

尽管类的成员被屏蔽了,仍然可以通过类名来限定成员名或显示使用this指针来使用它.

把fun代码修改如下:

int fun(double pg)
{
   return this->pg;
   //return  A::pg;
}

函数作用域之后,在类作用域中查找

如果想要使用pg成员, 更好的方式是为形参取一个不同的名字

int fun ( int b)

{

   return pg;

}

现在编译器会在A类中查找该名字, 尽管pg是现在fun中使用,然后在声明, 编译器还是确定这里用的是名为pg的数据成员.

类作用域之后,在外围作用域中查找

如果编译器不能在函数或作用域中找到,就在外围作用域中查找. 上面的例子中, 在类A定义之前的全局作用域中声明了一个名为pg的全局对象, 然后, 它被屏蔽了..

尽管全局对象被屏蔽了,但通过全局作用域确定操作符来限定名字,仍然可以使用它.

#include <iostream>
using namespace std;
const int   pg=10;
class A
{
   public:
      int  fun(double pg)
      {
          return  ::pg;  //这里使用全局作用域
          
      }
     
      A() : pg(0) { };
   private:
      double pg;
};

int main()
{
  int x;
  A test;
  x=test.fun(3);
  
  cout<<x<<endl;
  return 0;
} 


运行结果为10.

最后用课后习题总结一下:

解释下述代码.指出每次使用Type或initVal时用到的是哪个名字定义.如果存在错误,说明如何改正.

typedef string Type;
Type initVal();

class Exercise
{
   public: 
      typedef double Type;
      Type setVal( Type);
      Type initVal();
    private:
       int val;
};

Type Exercise::setVal( Type parm)
{
   val = parm + initVal();
}

成员函数setVal的定义有错. 进行必要的修改以便类Exercise使用全局的类型别名Type 和全局函数 initVal.


答案:

typedef string Type;  //定义全局的类型Type
Type initVal();  //全局的initVal的函数声明

class Exercise
{
   public: 
      typedef double Type; // 定义Exercise类内部的类型别名Type
      Type setVal( Type); //成员函数setVal的声明
      Type initVal();  //成员函数initVal的声明
    private: 
       int val;  //定义数据成员
   
};

Type Exercise::setVal( Type parm)
{
   val = parm + initVal();
}

 在Exercise类的定义体内, 成员函数setVal和initVal的声明中作为形参类型和返回类型的Type所使用的都是Exercise内部定义的类型别名Type.
  在Exercise类的定义体之外成员函数setVal的定义中, 作为形参类型的Type,使用的是Exercise内部定义的类型别名Type,作为函数返回类型的Type,使用的是全局类型的Type.
在Exercise类的成员函数setVal的定义中使用的initVal,用到的是Exercise类的成员函数.

成员函数setVal的定义有错, 编译器会认为Exercise类中存在两个相互重载的成员函数setVal,但这两个函数的区别仅在于返回类型不同,不能构成合法的函数重载,因此有错,而且该汉化素应返回一个值, 可更正为

Exercise::Type Exercise::setVal( Type parm)
{
  val=parm  + initVal();
  retrun val;
}

注:形参表和函数体处于类作用域中,而函数返回类型不一定在类作用域中, 所以要在返回类型面前加上作用域运算符.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值