C++ Primer 学习笔记_17_类与数据抽象(3)_类作用域

C++ Primer 学习笔记_17_类与数据抽象(3)_类作用域

引言:

每个类都定义了自己的新作用域与唯一的类型。即使两个类具有完全相同的成员列表,它们也是不同的类型。每个类的成员不同与任何其他类(或任何其他作用域)的成员

一、类作用域中的名字查找

1)首先,在使用该名字的块中查找名字的声明。只考虑在该项使用之前声明的名字。

2)如果在1)中找不到,则在包围的作用域中查找。

如果找不到任何声明,则出错。

类的定义实际上是在两个阶段中处理:

1)首先,编译器声明;

2)只有在所有成员出现之后,才编译他们的定义本身。

类作用域中说明的标识符只在类中可见。除了类作用域,还有块作用域、文件作用域、函数原型作用域、函数作用域,举个例子:

#include<iostream>
using namespacestd;

class Test
{
public:
    int num;
};

//num = 20;        Error,num的作用域在类内部
int num =20;      // num的作用域是文件作用域,与类中的num是不同的作用域

int add(int a,int b);  // a, b两个标识符的作用域为函数原型作用域

int main(void)
{
    int num = 30;      // num为块作域
    {
        int num = 100; // num为块作域
    }

    cout << num << endl;
    cout <<::num << endl;
    return 0;
}

int add(int a,int b)   // 形参a与b也算是块作用域
{
    return a + b;
}
int test()
{
LABEL1: //函数作用域
    cout << "label1" <<endl;
    goto LABEL3;
LABEL2:
    cout << "label2" <<endl;
    goto LABEL1;
LABEL3:
    cout << "label3" <<endl;
    goto LABEL2;
}

1、类成员声明的名字查找

必须是在类中先定义类型名字,才能将它们用作数据成员的类型,或者成员函数的返回类型或形参类型。一旦一个名字被用作类型名,则该名字就不能被重复定义:

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

private:
   typedef long double Money; //Error
   Money bal;
};

2、类成员定于中的名字查找

1)首先检查成员函数局部作用域中的声明。

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

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

3、类成员遵循常规的块作用域名字查找

在程序中尽量不要使用相同名字的形参和成员:

int height;
class Screen
{
public:
   typedef int index;

   //由于在函数形参中定义了height,
   //所以此处的height形参会屏蔽名为height的成员
   void dummy_fcn(index height)
    {
       cursor = width * height;
    }

private:
   index cursor;
   index height,width;
};

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

    void dummy_fcn(index height)
    {
        cursor = width * this -> height;
        //这两条语句作用相同
        cursor = width * Screen::height;
    }

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

如果想要使用height成员,最好的方式还是为形参定义一个不同的名字:

    void dummy_fcn(index ht)
    {
        cursor = width * height;
    }

尽管height是先在dummy_fcn中使用,然后再声明,但是编译器还是确定这里用的是名为height的数据成员。

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

尽管全局对象height在类中被数据成员height屏蔽了,但还可以通过全局作用域确定操作符来限定名字,仍然可使用它:

   void dummy_fcn(index height)
    {
       cursor = width * ::height
    }

6、嵌套类、局部类

(1)、嵌套类

外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现。

从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类之外的作用域使用该类名时,需要加名字限定。

嵌套类中的成员函数可以在它的类体外定义。

嵌套类的成员函数对外围类的私有成员没有访问权,反之亦然。

嵌套类仅仅只是语法上的嵌入

(2)、局部类

类也可以定义在函数体内,这样的类被称为局部类(localclass)。局部类只在定义它的局部域内可见。

局部类的成员函数必须被定义在类体中。

局部类中不能有静态成员,关于类中的静态成员和静态成员函数以后再谈。

#include <iostream>
using namespace std;

class Outer
{
public:
   class Inner
    {
   public:
       void Fun();
       //{
       //  cout<<"Inner::Fun..."<<endl;
       //}
   };
public:
   Inner obj;
   void Fun()
    {
       cout << "Outer::Fun ..." << endl;
       obj.Fun();
    }
};

void Outer::Inner::Fun()
{
   cout << "Inner::Fun ..." << endl;
}

void Fun()
{
   class LocalClass
    {
   public:
       int num_;
       void Init(int num)
       {
           num_ = num;
       }
       void Display()
       {
           cout << "num=" << num_ << endl;
        }

       //static int num2_; // 局部类内部不能定义静态成员
   };

   LocalClass lc;
   lc.Init(10);
   lc.Display();
}

int main(void)
{
   Outer o;
   o.Fun();

   Outer::Inner i;
   i.Fun();

   Fun();
   //LocalClass lc;        Error,局部类只能在定义它的函数体中使用
   return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-09-30 08:00:26

C++ Primer 学习笔记_17_类与数据抽象(3)_类作用域的相关文章

C++ Primer 学习笔记_15_类与数据抽象(1)_类的定义和声明

C++ Primer 学习笔记_15_类与数据抽象(1)_类的定义和声明 在C++中,用类来定义自己的抽象数据类型.通过定义类型来对应所要解决的问题中的各种概念,可以使我们更容易编写.调试和修改程序.可以使得自己定义的数据类型用起来与内置类型一样容易和直观. 看一下Sales_item类: class Sales_item { private: std::string isbn; unsigned units_sold; double revenue; public: double ave_pr

C++ Primer 学习笔记_17_从C到C++(3)--引用、const引用、引用传递、引用作为函数返回值、引用与指针区别

欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 一.引用 1.引用是给一个变量起别名 变量: 名称 空间 引用: 引用不是变量 引用仅仅是变量的别名 引用没有自己独立的空间 引用要与它所引用的变量共享空间 对引用所做的改变实际上是对它所引用的变量的改变 引用在定义的时候必须要进行初始化 引用一经初始化,不能重新指向其他变量 2.定义引用的一般格式: (1)类型  &引用名 = 变量名: (2)例如: int a=1; int &b=a; // b是a的别名,因此a和b是同一个单元 (3)

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++ Primer 学习笔记_56_类与数据抽象 --消息处理示例

复制控制 --消息处理示例 说明: 有些类为了做一些工作需要对复制进行控制.为了给出这样的例子,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message类和 Folder类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中.Message上有 save和 remove操作,用于在指定Folder中保存或删除该消息. 数据结构: 对每个Message,我们并不是在每个Folder中都存放一个副本,而是使每个Message保存一个指针集(set),set中

C++ Primer 学习笔记_57_类与数据抽象 --管理指针成员

复制控制 --管理指针成员 引言: 包含指针的类需要特别注意复制控制,原因是复制指针时只是复制了指针中的地址,而不会复制指针指向的对象! 将一个指针复制到另一个指针时,两个指针指向同一对象.当两个指针指向同一对象时,可能使用任一指针改变基础对象.类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在.指针成员默认具有与指针对象同样的行为. 大多数C++类采用以下三种方法之一管理指针成员: 1)指针成员采取常规指针型行为:这样的类具有指针的所有缺陷但无需特殊的复制控制! 2)类

C++ Primer 学习笔记_55_类与数据抽象 --析构函数

复制控制 --析构函数 引言: 在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源.析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充. 1.何时调用析构函数 撤销类对象时会自动调用析构函数: Sales_item *p = new Sales_item; { Sales_item item(*p); //调用复制构造函数 delete p; //调用指针p的析构函数 } //调用对象item的析构函数 动态分配的对象只有在指向该对象的指针被删除时才撤销,

C++ Primer 学习笔记_53_类与数据抽象 --友元、static成员

类 --友元.static成员 一.友元 友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类(对未被授权的函数或类,则阻止其访问):友元的声明以关键字friend开始,但是它只能出现在类定义的内部.友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的访问控制影响. [最佳实践] 通常,将友元声明成组的放在类定义的开始或结尾是个好主意! 1.友元关系:一个例子 假设一个窗口管理类Window_Mgr可能需要访问由其管理的Screen对象的内部

C++ Primer 学习笔记_54_类与数据抽象 --复制构造函数、赋值操作符

复制控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,需要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用!      复制构造函数:具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式的使用复制构造函数:当将该类型的对象传递给函数或者从函数返回该类型的对象时,将隐式使用复制构造函数.     析构函数:作为构造函数的互补,当对象超出作用域或动态分配的对象被删除

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式、auto_ptr与单例模式、const成员函数、const 对象、mutable修饰符

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式.auto_ptr与单例模式.const成员函数.const 对象.mutable修饰符 前言 [例]写出面向对象的五个基本原则? 解答:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则和里氏替换原则 里氏替换原则:子类型必须能够替换他们的基类型. 设计模式分为三种类型:创建型模式.结构型模式和行为型模式 一.static 与单例模式 1.单例模式 单例模式的意图:保证一个类仅有一个实例,并提供一个访问它