《随笔十三》—— C++中的 “ 名字查找与类的作用域 ”

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34536551/article/details/84633742

名字查找与类的作用域 (Primer 254)


● 名字查找:寻找与所用名字最匹配的声明的过程:

首先,在名字所在的块中寻找其声明语句, 只考虑在名字的使用之前出现的声明。

如果没有找到, 继续查找外层作用域。

如果最终没有找到匹配的声明,则程序报错。

定义在类内部的成员函数,解析其中的名字方式与上述查找规则有所区别,类的定义分两步:

首先,编译成员的声明 
直到类全部可见后才编译函数体

意思就是说,编译器处理完类中的全部声明后才会处理成员函数函数定义

通常非类作用域的符号查找是按顺序的,其后面的被涉及的符号必须在前面出现过.如:
void foo()
{
};
void main()
{
    int a= 1, b = 2;  
    int c = a + b; //涉及的a和b在前面出现过了.
    int d = foo(); //涉及的foo在前面出现过了.
    c = d + goo(); //编译报错,因为涉及的goo没有在前面出现过(其在本条语句的后面第一次出现)
}
void goo()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////
对于类作用域(即类成员)的符号,我们不必要符号顺序出现,如:
class CFoo
{
public:
    void hoo()
   {
        int c = m_aData + m_bData; //可以编译通过,虽然m_aData, m_bData声明在本条语句的后面,但是因为是类作用域,在类中都可见,所以这里是可以的.
   }
private:
    int m_aData,m_bData;
};
那么编译器是怎么实现类作用域的符号查找的呢?
分为两步编译:
第一步: .首先值编译类的所有成员的声明(而不是定义),如void CFoo::hoo()就是一个成员函数的声明,而其的函数体就是起定义.
  这样的话,在此步遍编译结束后,编译器会把所有的符号声明存储起来,供第二步使用.
第二步: 在编译成员的定义(如函数体),此时如果遇到一个变量如m_aData,就会首先从第一步中的存储查找。

● 按照这种两阶段的方式处理类可以简化类代码的组织方式。 因为成员函数体直到整个类可见后才会被处理, 所以它能使用类中定义的任何名字。 相反,如果函数的定义和成员的声明被同时处理,那么我们不得不在成员函数中使用那些早已出现的名字

●  注意: 这种两阶段的处理方式只适用于成员函数中的名字。 声明中使用的名字, 包括返回类型或者参数列表中使用的名字, 都必须在使用前确保可见。 如果某个成员的声明使用了类中尚未出现的名字, 则编译器将会在定义该类的作用域中继续查找。


类型名要特殊处理


●一般来说,内层作用域可以定义外层作用域的名字(隐藏了外层的名字) ,即使改名字已经在内层作用域使用过。在类中,如果成员使用了外层作用域中的某个名字, 而且该名字代表一种类型, 则类不能在之后重新定义该名字:

typedef double Money;
class Account
{
public:
    Money balance () //使用外层作用域的Money
    {
        return bal;
    }

private:
    typedef double Money;  // 错误:不能重新定义Money,但是在VS上编译是正确的,但行为确实错误的。
    Money bal;
};

注意: 类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后。


成员定义中的普通块作用域的名字查找


●  成员函数中使用的名字按照如下方式解析:

首先, 在成员函数内查找该名字的声明。 和前面一样, 只有在函数使用之前出现的声明才被考虑。

如果在成员函数内没有找到,则在类内继续查找,这时类的所有成员都可以被考虑。

如果类内也没找到该名字的声明,在成员函数定义之前的作用域内继续查找。

● 注意: 通常情况下不建议为参数(或者说局部变量)和成员使用同样的名字


在文件中名字的出现处对其进行解析


● 当成员定义在类的外部时,不仅要考虑类定义之前的全局作用域中的声明,还需要考虑在成员函数定义之前的全局作用域中的声明。

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);  //height 类的成员
}

猜你喜欢

转载自blog.csdn.net/qq_34536551/article/details/84633742
今日推荐