zoukankan      html  css  js  c++  java
  • c++基础(六)

    标识符的作用域与可见性

    • 作用域是一个标识符在程序正文中有效的区域。
    • 作用域分类
    1. 函数原型作用域
    2. 局部作用域(块作用域)
    3. 类作用域
    4. 文件作用域
    5. 命名空间作用域(详见第10章)

    函数原形作用域

    • 函数原型中的参数,其作用域始于"(",结束于")"。
    • 函数原形作用域举例:double area(double radius);

    局部作用域

    • 函数的形参、在块中声明的标识符;
    • 其作用域自声明处起,限于块中。
    • 局部作用域举例

    void fun(int a) {

       int b = a;

       cin >> b;

       if (b > 0) {

         int c;

     

         ......

       }

    }

    类作用域

    • 类的成员具有类作用域,其范围包括类体和非内联成员函数的函数体。
    • 如果在类作用域以外访问类的成员,要通过类名(访问静态成员),或者该类的对象名、对象引用、对象指针(访问非静态成员)。

    文件作用域

    • 不在前述各个作用域中出现的声明,就具有文件作用域,这样声明的标识符其作用域开始于声明点,结束于文件尾。

    可见性

    • 可见性是从对标识符的引用的角度来谈的概念
    • 可见性表示从内层作用域向外层作用域“看”时能看见什么。
    • 如果标识在某处可见,就可以在该处引用此标识符。
    • 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见。
    • 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。

    对象的生存期

    静态生存期

    • 这种生存期与程序的运行期相同。
    • 在文件作用域中声明的对象具有这种生存期。
    • 在函数内部声明静态生存期对象,要冠以关键字static 。

    动态生存期

    • 块作用域中声明的,没有用static修饰的对象是动态生存期的对象(习惯称局部生存期对象)。
    • 开始于程序执行到声明点时,结束于命名该标识符的作用域结束处。

    类的静态数据成员

    • 用关键字static声明
    • 为该类的所有对象共享,静态数据成员具有静态生存期。
    • 必须在类外定义和初始化,用(::)来指明所属的类

     案例:

    #include <iostream>
    using namespace std;
    class Point {     //Point类定义
    public:       //外部接口
        Point(int x = 0, int y = 0) : x(x), y(y) { //构造函数
            //在构造函数中对count累加,所有对象共同维护同一个count
            count++;
        }
        Point(Point &p) {    //复制构造函数
            x = p.x;
            y = p.y;
            count++;
        }
        ~Point() { count--; }
        int getX() { return x; }
        int getY() { return y; }
        void showCount() {           //输出静态数据成员
            cout << "  Object count = " << count << endl;
        }
    private:      //私有数据成员
        int x, y;
        static int count;       //静态数据成员声明,用于记录点的个数
    };
    int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
    int main() {       //主函数
        Point a(4, 5);     //定义对象a,其构造函数回使count增1
        cout << "Point A: " << a.getX() << ", " << a.getY();
        a.showCount(); //输出对象个数
        Point b(a); //定义对象b,其构造函数回使count增1
        cout << "Point B: " << b.getX() << ", " << b.getY();
        b.showCount();       //输出对象个数
        return 0;
    }

    上面案例中,我们需要记录下point类的对象的数目,我们不可能将这个变量存放于类的私有成员中,因为这样只能通过对象取访问,并且每声明一个类的对象,这个数目就要增加1。因此,我们可以将其定义为类的静态成员变量,这个变量是为所有的类对象共有的。但是,这样也会存在一个问题:当没有定义类的对象时,我们能不能知道类对象的个数呢?如果要通过类的对象来访问这个变量的话,那么需要先声明一个对象,但是类的对象还没有被声明,如何解决这个问题呢?这就需要用到类的静态函数成员了。

    类的静态函数成员

    • 类外代码可以使用类名和作用域操作符来调用静态成员函数。
    • 静态成员函数主要用于处理该类的静态数据成员(为什么?),可以直接调用静态成员函数。
    • 如果访问非静态成员,要通过对象来访问。

    将上面的程序稍作修改,如下:

    #include <iostream>
    using namespace std;
    class Point {
    public:
        Point(int x = 0, int y = 0) : x(x), y(y) { count++; }//构造函数
        Point(Point &p) {    //复制构造函数
            x = p.x;
            y = p.y;
            count++;
        }
        ~Point() { count--; }
        int getX() { return x; }
        int getY() { return y; }
        static void showCount() {
            cout << "  Object count = " << count << endl;
        }
    private:
        int x, y;
        static int count;       //静态数据成员声明,用于记录点的个数
    };
    int Point::count = 0;//静态数据成员定义和初始化,使用类名限定
    int main() {
        Point a(4, 5);     //定义对象a,其构造函数回使count增1
        cout << "Point A: " << a.getX() << ", " << a.getY();
        Point::showCount();       //输出对象个数
        Point b(a); //定义对象b,其构造函数回使count增1
        cout << "Point B: " << b.getX() << ", " << b.getY();
        Point::showCount();       //输出对象个数
        return 0;
    }

    我们在函数中增加了一个静态成员函数:showcount(),那么在访问类的静态成员变量时就可以不通过对象来访问,而是通过这个静态成员函数来访问,即使没有声明类的对象。但是静态成员函数不能直接访问对象的数据成员,而是要将对象传递给静态函数才可以访问。

    类的友元

    • 友元是C++提供的一种破坏数据封装和数据隐藏的机制。
    • 通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
    • 可以使用友元函数和友元类。
    • 为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。

    友元函数

    • 友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和protected成员
    • 作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
    • 访问对象中的成员必须通过对象名,也就是必须将类的对象作为参数传递给函数,一般我们选择引用传递,因为这样程序的开销会更小,但是引用时数据是双向传递的,因此可以在传参数时设定为常引用,这样就不会允许函数对传递进来的参数做修改了,保证了数据的安全性。

    友元类

    • 若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。
    • 声明语法:将友元类名在另一个类中使用friend修饰说明。
    • 类的友元关系是单向的:如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有、保护数据。

    共享数据的保护

    • 对于既需要共享、又需要防止改变的数据应该声明为常类型(用const进行修饰)。
    • 对于不改变对象状态的成员函数应该声明为常函数。

    常类型

    • 常对象:必须进行初始化,不能被更新。语法形式为:const 类名 对象名
    • 常成员:用const进行修饰的类成员,包括常数据成员和常函数成员
    • 常引用:被引用的对象不能被更新。语法格式为:const  类型说明符  &引用名
    • 常数组:数组元素不能被更新(详见第6章)。语法格式:类型说明符  const  数组名[大小]...
    • 常指针:指向常量的指针(详见第6章)。
    • 常对象:用const修饰的对象
    • 常成员:用const修饰的对象成员。常成员包括常成员函数和常数据成员,使用const关键字说明的函数。常成员函数不更新对象的数据成员。常成员函数说明格式:类型说明符  函数名(参数表)const;这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。const关键字可以被用于参与对重载函数的区分;通过常对象只能调用它的常成员函数,非常对象也可以调用常成员函数,因此,如果在类中含有类似于显示数据成员这样功能的函数,可以将其定义为常函数,以确保数据成员的安全性。第二种常成员是常数据成员,它是使用const说明的数据成员。常数据成员可以在定义的时候初始化,这是在c++11标准中的新规定,也可以使用构造函数的初始化列表进行初始化,常数据成员不可以被改变。

     

    常引用

    • 如果在声明引用时用const修饰,被声明的引用就是常引用。
    • 常引用所引用的对象不能被更新。
    • 如果用常引用做形参,便不会意外地发生对实参的更改。常引用的声明形式如下:

    const  类型说明符  &引用名;

    多文件结构和编译预处理命令

    C++程序的一般组织结构

    一个工程可以划分为多个源文件:

    1、  类声明文件(.h文件);

    2、  类实现文件(.cpp文件);

    3、  类的使用文件(main()所在的.cpp文件)。

    利用工程来组合各个文件。

    外部变量

    如果一个变量除了在定义它的源文件中可以使用外,还能被其它文件使用,那么就称这个变量是外部变量。

    文件作用域中定义的变量,默认情况下都是外部变量,但在其它文件中如果需要使用这一变量,需要用extern关键字加以声明。

    外部函数

    在所有类之外声明的函数(也就是非成员函数),都是具有文件作用域的。这样的函数都可以在不同的编译单元中被调用,只要在调用之前进行引用性声明(即声明函数原型)即可。也可以在声明函数原型或定义函数时用extern修饰,其效果与不加修饰的默认状态是一样的。

    将变量和函数限制在编译单元内

    使用匿名的命名空间:在匿名命名空间中定义的变量和函数,都不会暴露给其它的编译单元。

       namespace {         //匿名的命名空间

             int n;

             void f() {

                         n++;

             }

       }

    这里被“namespace { …… }”括起的区域都属于匿名的命名空间。

    标准C++库:标准C++类库是一个极为灵活并可扩展的可重用软件模块的集合。标准C++类与组件在逻辑上分为6种类型:

    输入/输出类

    容器类与抽象数据类型

    存储管理类

    算法

    错误处理

    运行环境支持

    编译预处理

    #include 包含指令:将一个源文件嵌入到当前源文件中该点处。

    #include<文件名>:按标准方式搜索,文件位于C++系统目录的include子目录下

    #include"文件名":首先在当前目录中搜索,若没有,再按标准方式搜索。

    #define 宏定义指令:定义符号常量,很多情况下已被const定义语句取代。定义带参数宏,已被内联函数取代。

    #undef:删除由#define定义的宏,使之不再起作用。

    条件编译指令——#if 和 #endif

    #if  常量表达式

     //当“ 常量表达式”非零时编译

         程序正文 

    #endif

    ......

    条件编译指令——#else

      #if   常量表达式

         //当“ 常量表达式”非零时编译

           程序正文1

    #else

      //当“ 常量表达式”为零时编译

           程序正文2

    #endif

    条件编译指令——#elif

    #if 常量表达式1

        程序正文1  //当“ 常量表达式1”非零时编译

    #elif 常量表达式2

        程序正文2  //当“ 常量表达式2”非零时编译

    #else

        程序正文3  //其他情况下编译

    #endif

    条件编译指令

    #ifdef 标识符

        程序段1

    #else

        程序段2

    #endif

    如果“标识符”经#defined定义过,且未经undef删除,则编译程序段1;否则编译程序段2。

    #ifndef 标识符

       程序段1

    #else

       程序段2

    #endif

    如果“标识符”未被定义过,则编译程序段1;否则编译程序段2

  • 相关阅读:
    php 程序执行时间检测
    Mybatis里@InsertProvider、@UpdateProvider方法里使用if test标签
    windows配置JAVA环境变量
    谈谈2021,以及未来的几年
    jupyterlab内终端的改变
    RuntimeError: CUDA error: device-side assert triggered的解决
    Jupyter notebook添加内核的正确姿势
    Windows上两个host文件的位置
    Linux cp 命令仅复制文件
    Pytorch显卡设置
  • 原文地址:https://www.cnblogs.com/puheng/p/9296925.html
Copyright © 2011-2022 走看看