OSX + Xcode + C++ (9)

数据共享与保护

1. 标识符的作用域

按照由小到大的顺序,作用域分为:函数原型作用域->块作用域->类作用域->文件作用域->命名空间作用域。

函数原型作用域函数原型中的形参表
局部作用域函数定义的形参、函数内部定义的变量
类作用域类体、类外实现的成员函数等
文件作用域除上述情况外

我们知道,在声明函数原型声明时,可以只给参数类型,而不给参数名,因为在参数列表中不需要使用这个参数名,但是为了完整性可用性,一般还是要加上参数名。

文件作用域起始于定义点,在整个文件结束之前都有效。

2. 标识符的可见性

所谓可见性,是指从内层作用域向外看时,能看到什么。如果该标识符在某处可见,就可以在该处引用该标识符。
层数的划分如下所示:
这里写图片描述
可见性的规则是:
1. 如果某标识符在外层中声明了,且内层中没有同名标识符,那么该标识符在内层中是可见的;
2. 如果内层中有同名标识符,那么该标识符在内层中被屏蔽,也就不可见。

3. 对象的生存期

对象的生存期,就是从对象诞生开始到它的消亡存在的期间。

3.1 静态生存期

静态生存期是指与程序的运行期相同,也就是说,在程序开始到结束期间,一直存在于内存中。
1. 在文件作用域中生成的对象具有静态生存期;
2. 在函数内部以static关键字声明的对象也具有静态生存期。

3.2 动态生存期

动态生存期是指从对象声明开始,到命名该标识符的作用域的结束处,也就是紧紧包围着它的大括号。
块作用域中声明的,且没有用static关键字标识的对象具有动态生存期。
来看以下示例程序:

//
//  main.cpp
//  VisableAndLively
//
//  Created by Evelyn on 2018/8/7.
//  Copyright © 2018年 Evelyn. All rights reserved.
//

#include <iostream>
using namespace std;
//全局变量,具有静态生存期
int i = 1;

void other() {
    //静态局部变量,具有静态生存期,局部可见性,
    //只在第一次进入函数时被初始化,之后一直存在,
    //即使程序执行流程离开其所在的作用域,依然存
    //于内存中,下次进入该函数时还能保持原来的值。
    static int a = 2, b;
    //局部变量,动态生存期,局部可见性,程序执行流
    //程离开其所在作用域后就消亡,再次进入时被重新
    //初始化
    int c = 0;
    a += 2;
    i += 32;
    c += 5;
    cout << "------other()----------" << endl;
    cout << "i: " << i << " a: " << a << " b: " << b;
    cout << " c: " << c << endl;
    b = a;
}

int main(int argc, const char * argv[]) {
    //静态局部变量,具有静态生存期,局部可见性,静态
    //变量有默认初始值,int型是0,char型是''等,而动态
    //变量是没有默认初始值,如果不给赋值就引用,会报错。
    static int a;
    //局部变量,动态生存期,拒不可见
    int b = -10;
    int c = 0;
    cout << "------main()----------" << endl;
    cout << "i: " << i << " a: " << a << " b: " << b;
    cout << " c: " << c << endl;
    c += 8;
    other();
    cout << "------main()----------" << endl;
    cout << "i: " << i << " a: " << a << " b: " << b;
    cout << " c: " << c << endl;
    i += 10;
    other();
    return 0;
}

程序的运行结果如下:

------main()----------
i: 1 a: 0 b: -10 c: 0
------other()----------
i: 33 a: 4 b: 0 c: 5
------main()----------
i: 33 a: 0 b: -10 c: 8
------other()----------
i: 75 a: 6 b: 4 c: 5

4. 类的静态成员

4.1 类的静态数据成员

有的时候,类具有的某种属性,不单独属于每一个对象,这时把该属性作为类的对象的属性来存储就不太合适了,c++提供了类的静态数据成员的机制来解决这个问题。
例如之前已经用到过的Point类,假设这个类有一个属性count表示所有点的个数,显然这个属性不适合于作为每一个点的属性,这时我们可以构造一个静态成员count来保存这个属性:

class Point {
public:
    //default constructor
    Point();
    //normal constructor
    Point(int x, int y);
    //copy constructor
    Point(const Point& p);
    //析构函数
    ~Point();
    void showCount();
private:
    int x, y;
    //静态数据成员
    static int count;
};

//静态数据成员的初始化必须在类外
int Point::count = 0;
Point::Point(int newX, int newY) {
    x = newX;
    y = newY;
    count++;
};
Point::Point():Point(0, 0) {};
Point::Point(const Point& p) {
    x = p.x;
    y = p.y;
    count++;
    cout << "calling copy constructor of point...\n";
};
Point::~Point() {
    count--;
};

void Point::showCount() {
    cout << "Point number is : " << count << endl;
};
int main(int argc, const char * argv[]) {
    Point p1(1, 2), p2(4, 5);
    p1.showCount();
    p2.showCount();
    return 0;
}

我们可以看到,类的动态数据成员和静态数据成员的区别:
1. 每个点都有x值和y值,但是每个点不单独存储count,只在类中存储一份。
2. 动态数据成员在初始化对象时赋值,静态数据成员在类外进行初始化。

4.2 类的静态函数成员

考虑上一个例子,我们知道,类的对象p1和p2都可以访问类的静态成员count,但是当没有定义类的对象时,我们想知道count的值是多少,应该怎么办呢?首先,count是私有成员,无法从外部访问;其次,showCount函数也必须通过对象来调用它,因此,上面的例子是无法完成这个功能的。为了对类的静态数据成员进行操作,c++提供了静态函数成员的机制。
我们对上述程序做简单修改:

class Point {
public:
    //default constructor
    Point();
    //normal constructor
    Point(int x, int y);
    //copy constructor
    Point(const Point& p);
    //析构函数
    ~Point();
    //静态函数成员
    static void showCount();
private:
    int x, y;
    //静态数据成员
    static int count;

};
class Line {
public:
    //default constructor
    Line();
    //normal constructor
    Line(Point p1, Point p2);
    //copy constructor
    Line(const Line& l);
private:
    Point p1, p2;
};
//静态数据成员的初始化必须在类外
int Point::count = 0;
Point::Point(int newX, int newY) {
    x = newX;
    y = newY;
    count++;
};
Point::Point():Point(0, 0) {};
Point::Point(const Point& p) {
    x = p.x;
    y = p.y;
    count++;
    cout << "calling copy constructor of point...\n";
};
Point::~Point() {
    count--;
};
//static 关键字只能出现在类的声明中,实现时不能添加
void Point::showCount() {
    cout << "Point number is : " << count << endl;
};

int main(int argc, const char * argv[]) {
    //直接通过类名访问静态函数成员
    Point::showCount();
    // insert code here...
    Point myp1(1, 2), myp2(4, 5);
    Point::showCount();
    //也可以通过对象名来访问静态函数成员
    myp2.showCount();
    return 0;
}

5. 类的友元

5.1 友元函数

类的友元函数是指不属于类的成员函数的函数,需要在类声明中使用friend关键字声明,在它的函数体中能够通过对象名访问private和protected成员。其作用是为了增加灵活性,提高效率。
例如,假设有一个功能需要频繁的计算两个点之间的距离,这时候如果使用getX()和getY()就需要不断地在函数之间切换,造成比较大的开销,如果我们定义一个友元函数如下:


class Point {
public:
    //default constructor
    Point();
    //normal constructor
    Point(int x, int y);
    //copy constructor
    Point(const Point& p);
    //析构函数
    ~Point();
    static void showCount();
    //在类声明中使用friend关键字声明友元函数,参数使用
    //引用而非传对象名,因为引用就是指针,也就是效率高
    friend double distance(Point& a, Point& b);
private:
    int x, y;
    //静态数据成员
    static int count;

};
class Line {
public:
    //default constructor
    Line();
    //normal constructor
    Line(Point p1, Point p2);
    //copy constructor
    Line(const Line& l);
private:
    Point p1, p2;
};
//静态数据成员的初始化必须在类外
int Point::count = 0;
Point::Point(int newX, int newY) {
    x = newX;
    y = newY;
    count++;
};
Point::Point():Point(0, 0) {};
Point::Point(const Point& p) {
    x = p.x;
    y = p.y;
    count++;
    cout << "calling copy constructor of point...\n";
};
Point::~Point() {
    count--;
};

void Point::showCount() {
    cout << "Point number is : " << count << endl;
};
//
double distance(Point &a, Point &b) {
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
int main(int argc, const char * argv[]) {
    Point::showCount();
    // insert code here...
    Point myp1(1, 2), myp2(4, 5);
    Point::showCount();
    myp2.showCount();
    cout << distance(myp1, myp2);
    return 0;
}

5.2 友元类

同友元函数类似,友元类就是把一个类声明为另一个类的友元。这样前一个类的所有成员都能访问后一个类的私有成员。
另外,友元关系是单向的。

6. 常类型

6.1 常对象

  1. 常对象就是使用const关键字声明的类的对象,必须被初始化,并且初始化之后不能改变对象的值。
  2. 常数据成员是对象的成员,也可以是类(static)的成员,如果是对象的成员,必须在初始化对象时对其赋值,如果是类的成员,需要在类外进行初始化。
  3. 常函数成员是专门用来处理常对象的函数,在声明时会在后面加const关键字,一个函数如果被声明为常函数,编译器就会对他进行检查,确保其中的操作没有改变对象的属性时才能正确通过编译。
  4. 常引用实现函数调用时数据传递的机制,即使用引用提高数据传递效率,又使用const限定符保证数据不会双向传递。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值