C++基础(6)——数据的共享与保护

C++基础(6)——数据的共享与保护

  1. 变量和对象定义在不同的位置(函数体内、类体内、函数原型参数表内、所有函数和类之外)。

    其作用域、可见性、生存期都不同。

  2. 属于整个类的数据成员——静态数据成员

    用于处理静态数据成员的函数——静态成员函数

  3. 友元:对一些类外的函数、其他的类,给预授权,使之可以访问类的私有成员。

  4. 通过const关键字,限制对共享数据的修改,只能利用。

  5. 编译预处理/多文件结构

一、标识符的作用域与可见性
1. 作用域分类
  • 函数原型作用域:函数原型中的参数,其作用域始于"( “,结束于” ) "。

    举例: double area(double radius); //radius的作用域尽在于此,不能用于程序正文其他地方

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

    局部作用域举例
    void fun(int a){    //整个函数体均为a的作用域
        int b = a;    //从b定义至整个函数体为b的作用域
        cin >> b;
        if(b > 0){
            int c;    //if语句括号内为c的作用域}
    }
    
  • 类作用域:类的成员具有类作用域,其范围包含类体和成员函数体。

    在类作用域以外访问类的成员:

    • 静态成员:通过类名,或者该类的对象名、对象引用访问。
    • 非静态成员:通过类名,或者该类的对象名、对象引用、对象指针访问。
  • 文件作用域:不在前述各个作用域出现的声明,就具有文件作用域;

    其作用域开始于声明点,结束于文件尾。

  • 命名空间作用域

  1. 可见性

    可见性是从对标识符的引用的角度来谈的概念。

    • 可见性表示从内层作用域向外层作用域向外层作用域“看”时能看见什么。如果标识在某处可见,就可以在该处引用此标识符。
    • 命名空间作用域 > 类作用域 > 块作用域
    • 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见。
    • 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。
#include<iostream>
using namespace std;

int i;    //全局变量,文件作用域
int main(){
    i = 5;    //为全局变量i赋值
    {
        int i;    //局部变量,局部作用域
        i = 7;   
        cout << i << endl;  //输出7
    }
    cout << i <<endl;  //输出5
    return 0;
}
二、对象的生存期
  1. 对象从产生到结束的这段时间就是它的生存期。

    在对象生存期内,对象将保持它的值,直到被更新为止。

  2. 静态生存期

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

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

int i = 1;  //i为全局变量,具有静态生存期,在子函数和主函数中变化都会影响它的值
void other(){
    static int a = 2;
    static int b;  //a b为静态局部变量,具有全局寿命,局部可见。只第一次进入函数时被初始化。
    int c = 10;  //c为局部变量,具有动态生存期。每次进入函数时都初始化。
    a += 2;
    i += 32;
    c += 5;
    cout << "---OTHER---\n" ;
    cout << i << a << b << c << endl;
    b = a;
}

int main(){
    static int a;  //静态局部变量,有全局寿命,局部可见。
    int b = -10;  //b c为局部变量,具有动态生存期
    int c = 0;
    cout << "---MAIN---\n" ;
    cout << i << a << b << c << endl;
    c += 8;
    other();
    cout << "---MAIN---\n" ;
    cout << i << a << b << c << endl;
    i += 10;
    other();
    return 0;
}

运行结果:
---MAIN---
1 0 -10 0
---OTHER---
33 4 0 15
---MAIN---
33 0 -10 8
---OTHER---
75 6 4 15
三、静态数据成员

用关键字static声明;

为该类的所有对象共享,静态数据成员具有静态生存期;

必须在类外定义和初始化,用(::)来指明所属的类。

例:具有静态数据成员的Point类

Point
- x : int
- y : int
- count : int = 0(静态数据成员)
+ Point(xx : int =0, yy : int =0)
+ getX() : int
+ getY() : int
+ Point( p : Point &)
+ showCount() : void

例:静态数据成员声明、定义和初始化

#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--;}  //析构函数,每个点消亡的时候给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 A:4,5  Object count = 1
Point B:4,5  Object count = 2
四、类的友元
  1. 友元是C++提供的一种破坏数据封装和数据隐藏的机制。

    • 通过将一个模块声明为另一模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息;
    • 可以声明友元函数和友元类
    • 为了确保数据的完整性以及数据封装与隐藏的原则,建议慎用友元。
  2. 友元函数
    • 友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体内能够通过对象名访问private和protected成员
    • 作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择;
    • 访问对象中的成员必须通过对象名
    //例 使用友元函数计算两点间的距离。
    
    #include <iostream>
    #include<cmath>
    
     class Point{
         public:
           Point(int x=0,int y=0):x(x),y(y){}
           int getX(){return x;}
           int getY(){return y;}
           friend float dist(Point &a,Point &b);   //友元函数声明
         private:
           int x,y;
     };
     
     float dist(Point &a,Point &b){  //float dist(const Point &a,const Point &b)常引用,值不改变
         double x = a.x - b.x;  //通过对象名访问私有成员
         double y = a.y - b.y;
         return static_cast<float>(sqrt(x*x+y*y));
     }
     
     int main(){
         Point p1(1,1),p2(4,5);
         cout << "The distance is ";
         cout << dist(p1,p2) << endl;
         return 0;
     }
     
    运行结果:
    The distance is 5
    
3. 友元类
  • 若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员
  • 声明语法:将友元类名在另一个类中使用friend修饰说明。
  • 类的友元关系是单向的:声明B类是A类的友元 ≠ A类是B类的友元。
//友元类举例
class A{
    friend class B;
    public:
      void display(){
          cout << x << endl;
      }
    private:
      int x;
};

class B{
    public:
      void set(int i);
      void display();
    private:
      A a;
};

void B::set(int i){
    a.x =i;
}
void B::display(){
    a.display();
}

五、共享数据的保护
常类型

1. 常对象:必须进行初始化,不能被更新。 const 类名 对象名;

2. 常成员:用const进行修饰的类成员:常数据成员和常函数成员。

  • 常成员函数:

    • 使用const关键字说明的函数;

    • 常成员函数不更新对象的数据成员;

    • 常成员函数说明格式: 类型说明符 函数名(参数表) const;

      这里的const是函数类型的一个组成部分,因此在实现部分也要带const关键字。

    • const关键字可以被用于参与对重载函数的区分;

    • 通过常对象只能调用它的常成员函数。

  • 常数据成员:使用const说明的数据成员。

//常成员函数举例

#include<iostream>
using namespace std;

class R{
    public:
      R(int r1, int r2):r1(r1),r2(r2){}
      void print();
      void print() const;
    private:
      int r1,r2;
};

void R::print(){
    cout << r1 << ":" << r2 << endl;
}
void R::print() const{
    cout << r1 << ";" << r2 << endl;
}

int main(){
    R a(5,4);
    a.print();  //调用void print()
    const R b(20,52);
    b.print();  //调用void print() const
    return 0;
}

运行结果:
5:4
20;52
  1. 常引用:被引用的对象不能被更新。 const 类型说明符 &引用名;

    在友元函数中用常引用做参数,既能获得较高的执行效率,又能保证实参的安全性。

  2. 常数组:数组元素不能被更新。 类型说明符 const 数组名[大小]…

  3. 常指针:指向常量的指针。

六、多文件结构和编译预处理
  1. C++程序的一般组织结构

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

    • 类声明文件(.h文件)
    • 类实现文件(.cpp文件)
    • 类的使用文件(main()函数所在的.cpp文件)

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

//例 多文件工程

//文件1.类的定义,Point.h
class Point{  //Point类的定义
    public:  //外部接口
    Point(int x = 0, int y = 0:x(x),y(y)){  //构造函数
        count ++;  //在构造函数中对count累加,所有对象共同维护同一个count
    }
    Point(Point &p);
    ~Point(){count--;}  //析构函数,每个点消亡的时候给count减一
    int getX(){return x;}
    int getY(){return y;}
    void showCount()private:  //私有数据成员
      int x,y;
      static int count;  //静态数据成员声明,用于记录点的个数!!!!!
}//文件2,类的实现,Point.cpp
#include"Point.h"  //用双引号引起来,是自己定义的
#include<iostream>  //用<>引起来,在系统约定的安装目录下寻找文件
using namespace std;

int Point::count = 0;  //静态数据成员定义和初始化,使用类名限定!!!!!

Point::Point(Point &p):x(p.x),y(p.y){  //复制构造函数
        count ++;
    }
 
void Point::showCount(){  //输出静态数据成员
        cout << "Object count = " << count << endl;
    }
    
//文件3,主函数,5_10.cpp
#include"Point.h"  
#include<iostream>
using namespace std;

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;
}
2. 外部变量
  • 除了在定义它的源文件下可以使用外,还能被其他文件使用;
  • 文件作用域中定义的变量,默认情况下都是外部变量;
  • 在其他文件中如果需要使用,需要用extern关键字声明。
3. 外部函数
  • 在多有类之外声明的函数(也就是非成员函数)都是具有文件作用域的;
  • 这样的函数可以在不同编译单元中被调用;
  • 只要在调用之前进行引用性声明(即声明函数原型)即可。
4. 将变量和函数限制在编译单元内

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

namespace{  //匿名的命名空间
    int n;
    void f(){
        n++;
    }
}
5. 标准C++库
  • 标准C++库是一个极为灵活并可扩展的可重用软件模块的集合。
  • 标准C++类与组件在逻辑上分为6种类型:
    • 输入/输出类
    • 容器类与抽象数据类型
    • 存储管理类
    • 算法
    • 错误处理
    • 运行环境支持
6. 编译预处理
  • #include包含指令
    • 将一个源文件嵌入到当前源文件中该点处;

    • #include<文件名> 按标准方式搜索,文件位于C++系统目录的include子目录下
    • #include<文件名> 首先在当前目录中搜索,若没有,再按标准方式搜索。
  • #define宏定义指令

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

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

    // 1.条件编译指令 —— #if和#endif
    #if 常量表达式
        程序正文  //当“常量表达式”非0时编译
    #endif
    
    // 2. 条件编译指令 —— #else
    #if 常量表达式1
        程序正文1  //当“常量表达式”非0时编译
    #else
        程序正文2  //当“常量表达式”为0时编译
    #endif
    
    // 3. 条件编译指令 —— #elif
    #if 常量表达式1
        程序正文1  //当“常量表达式1”非0时编译
    #elif
        程序正文2  //当“常量表达式2”非0时编译
    #else
        程序正文3  //其他情况下编译
    #endif
            
    
    // 4. 条件编译指令
    #ifdef 标识符
        程序段1
    #else
        程序段2
    #endif
    //如果“标识符”经#define定义过,且未经undef删除,则编译程序段1;否则编译程序段2。
    
    #ifndef 标识符  //多了一个n,no
        程序段1
    #else
        程序段2
    #endif
    //如果“标识符”未经#define定义过,则编译程序段1;否则编译程序段2。
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值