C++语言程序设计——知识点复盘(第五章 数据的共享与保护)

目录

标识符的作用域

1、函数原型作用域

2、局部作用域

3、类作用域

4、命名空间作用域(文件作用域)

标识符的可见性

对象的生存期

1、静态生存期

2、动态生存期

例.变量的生存期与可见性

 总结

 类的静态成员

1、静态数据成员

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

 2、静态函数成员

1、友元函数

例.使用友元函数计算两点间的距离

 2、友元类

例.B类是A类的友元类

共享数据的保护

常对象

常成员函数

常数据成员

常引用

多文件结构

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

外部变量

外部函数

编译预处理

1、#include指令

2、#define和 #undef指令

3、条件编译指令

4、defined操作符


标识符的作用域

是指一个标识符在程序正文中有效的区域。C++中包括以下几种作用域:

1、函数原型作用域

声明函数原型时形式参数的作用范围,是C++程序中最小的作用域。

double area(double r);

上例中的左右括号之间就是r的作用域。

2、局部作用域

(1)函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。

(2)函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的大括号为止。(所谓,就是一对大括号括起来的一段程序)

具有局部作用域的变量也称为局部变量

3、类作用域

4、命名空间作用域(文件作用域)

一个命名空间确定了一个命名空间作用域,凡是在该命名空间之内声明的、不属于前面所述各个作用域的标识符,都属于该命名空间作用域。格式如下:

namesapce 命名空间名{
    命名空间内的各种声明(函数声明、类声明、...)
}

在命名空间内部可以直接引用当前命名空间中声明的标识符,若需要应用其他命名空间的标识符,需要使用下面的语法:

using 命名空间名::标识符名;
using namespace 命名空间名;

前一种形式将指定的标识符暴露在当前的作用域内,使得在当前作用域中可以直接引用该标识符;后一种形式将指定命名空间内的所有标识符暴露在当前的作用域内。

具有命名空间作用域的变量也称为全局变量


标识符的可见性

即标识符的有效范围,表示从内层作用域向外层作用域“看”时能看到什么。程序运行到某一点,能够引用到的标识符,就是该处可见的标识符。

C++语言程序设计——知识点复盘(第五章 数据的共享与保护)_第1张图片

 可见性的一般规则如下:

1、标识符要声明在前,应用在后。

2、在同一作用域中,不能声明同名的标识符。

3、在没有互相包含关系的不同的作用域中声明的同名标识符,互不影响。

4、如果在两个或多个具有包含关系的作用域中声明了同名标识符,则外层标识符在内层不可见。


对象的生存期

 即寿命,指对象(包括简单变量)从诞生到消失的这段时间。

1、静态生存期

如果对象的生存期与程序的运行期相同,则称它具有静态生存期,该对象称为静态变量。包括以下两种:(1)命名空间作用域中的对象。

           (2)使用关键字static。

局部作用域中静态变量地特点是,它并不会随着每次函数调用而产生一个副本,也不会随着函数返回而失效。

注:定义时未指定初值的基本类型静态生存期变量,会被赋予0值初始化,而对于动态生存期变量,不指定初值意味着初值不确定。另,若定义数组时未初始化,初值也不确定;若定义数组时初值个数少于数组大小,其余自动为0。

2、动态生存期

除了以上两种情况,其余的对象都具有动态生存期,称为局部生存期对象。局部生存期对象诞生于声明点,结束语声明所在的块执行完毕之时。

例.变量的生存期与可见性

#include
using namespace std;
int i = 1;//静态全局变量

void other() {
	static int a = 2, b;//静态局部变量:全局寿命、局部可见,只有第一次进入函数时被初始化
	int c = 10;//动态局部变量:局部寿命、局部可见,每次进入函数时都初始化
	a += 2;
	i += 32;
	c += 5;
	cout << "---OTHER---" << endl;
	cout << "i:" << i << "a:" << a << "b:" << b << "c:" << c << endl;
	b = a;
}

int main() {
	static int a;//静态局部变量:全局寿命、局部可见
	int b = -10, c = 0;//动态局部变量:局部寿命、局部可见
	cout << "---MAIN---" << endl;
	cout << "i:" << i << "a:" << a << "b:" << b << "c:" << c << endl;
	c += 8;
	other();
	cout << "---MAIN---" << endl;
	cout << "i:" << i << "a:" << a << "b:" << b << "c:" << c << endl;
	i += 10;
	other();
	return 0;
}

运行结果如下: 

C++语言程序设计——知识点复盘(第五章 数据的共享与保护)_第2张图片

 总结

静态局部变量——全局寿命、局部可见

动态局部变量——局部寿命、局部可见

全局变量都是静态的,所以——全局寿命、全局可见


 类的静态成员

1、静态数据成员

如果某个属性为整个类所共有,不属于任何一个对象,则采用static关键字来声明为静态成员。静态成员在每个类只有一个副本,由该类的所有对象共同维护和使用,从而实现了同一类的不同对象之间的数据共享。

静态数据成员具有静态生存期。由于静态数据成员不属于任何一个对象,因此可以通过类名对它进行访问,一般的用法是“类名::标识符”。在类的定义中只对静态数据成员进行引用性声明,然后在命名空间作用域中必须使用类名进行定义性声明。之所以类的静态数据成员需要在类定义之外再加以定义,是因为需要以这种方式专门为它们分配空间。

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

#include
using namespace std;
int i = 1;//静态全局变量

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; }
	void showCount() {
		cout << "count=" << count << endl;
	}
public:
	int x, y;
	static int count;
};

int Point::count = 0;//定义时不用加static

int main() {
	Point a(4, 5);
	a.showCount();
	Point b(a);
	b.showCount();
	return 0;
}

注:在int Point::count=0;这条语句中,虽然这个静态数据成员是私有类型,在这里却可以直接初始。除了这种场合,在其他地方就不允许直接访问了。

 2、静态函数成员

就是使用static关键字声明的函数成员,性质与静态数据成员一样。静态成员函数可以直接访问该类的静态数据和函数成员,而访问非静态成员必须通过对象名。因此通过静态函数成员访问非静态成员是相当麻烦的,一般情况下,它主要用来访问同一个类中的静态数据成员,维护对象之间共享的数据。

#include
using namespace std;

class A {
public:
	static void f(A a);
private:
	int x;
};
void A::f(A a) {
	cout << x;//错误!
	cout << a.x;
}

C++语言程序设计——知识点复盘(第五章 数据的共享与保护)_第3张图片


类的友元

友元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。通俗来说,友元关系就是一个类主动声明哪些其他类或函数是它的朋友,进而给它们提供对本类的访问特许。从一定程度上来讲,友元是对数据隐蔽和封装的破坏。

友元类的所有成员函数都自动成为友元函数。

1、友元函数

友元函数是在类中用关键字friend修饰的非成员函数。友元函数可以是一个普通的函数,也可以是其他类的成员函数。虽然它不是本类的成员函数,但是在它的函数体中可以通过对象名访问类的私有和保护成员。

例.使用友元函数计算两点间的距离

#include
#include
using namespace std;

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 &p1, Point &p2);//友元函数声明
private:
	int x, y;
};

float dist(Point &p1, Point &p2) {//友元函数实现
	double x = p1.x - p2.x;
	double y = p1.y - p2.y;
	return (float)(sqrt(x*x + y*y));
}

int main() {
	Point p1(1, 1), p2(4, 5);
	cout << "The distance is:" << dist(p1, p2) << endl;
	return 0;
}

 2、友元类

若A类为B类的友元类,则A类的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员。声明的语法形式为:

class B{
    ...
    friend class A;//声明A为B的友元类
    ...
};

例.B类是A类的友元类

#include
using namespace std;

class A {
public:
	void display() { cout << x << endl; }
	int getX() { return x; }
	friend class B;//B类是A类的友元类
private:
	int x;
};

class B {
public:
	void set(int i);
private:
	A a;
};
void B::set(int i) {
	a.x = i;//由于B是A的友元,所以在B的成员函数中可以访问A类对象的私有成员
}

关于友元,还有几点需要注意:

(1)友元关系是不能传递的。

(2)友元关系是单向的。

(3)友元关系是不能继承的。


共享数据的保护

对于既需要共享又需要防止改变的数据应该声明为常量。常量的重要特性:必须初始化,而且不能被更新。

常对象

它的数据成员值在对象的整个生存期间不能被改变;不能通过常对象调用普通的成员函数。

常成员函数

不会改变目的对象的数据成员的值;常、非常数据都可用;目的对象都被视为常对象。

常数据成员

任何函数都不能对其赋值,只能在初始化列表中初始化。

常引用

引用的对象不能更新,且自动化为常对象;非const引用只能绑定普通对象,const引用都可以绑定。

常引用做形参:为了追求高效率,且不双向传递。对于在函数中无需改变其值的参数,不宜使用普通引用方式传递,因为那会使得常对象无法被传入,采用传值方式或传递常引用的方式可避免这一问题;复制构造函数的参数一般也宜采用常引用传递。


多文件结构

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

在规模较大的项目中,往往需要多个源程序文件,每个源程序文件称为一个编译单元,C++语法要求一个类的定义必须出现在所有使用该类的编译单元中,一般将类的定义写在头文件中,私用该类的编译单元则包含这个头文件。通常一个项目至少划分为3个文件:类定义文件(*.h文件)、类实现文件(*.cpp文件)、类使用文件(*.cpp主函数文件)。

指令include有两种书写方式:

#include<文件名>表示按照标准方式搜索要嵌入的文件,该文件位于编译环境的include子目录下。一般要嵌入系统提供的标准文件时采用这样的方式,如对标准头文件iostream的包含。

#include"文件名"表示首先在当前目录下搜索要嵌入的文件,如没有,再按照标准方式搜索。一般对用户自己编写的文件采用这种方式。

决定一个声明放在源文件还是头文件中的一般原则是:将需要分配空间的定义放在源文件中,而将不需要分配空间的声明放在头文件中。

外部变量

即除了在定义它的源文件中可以使用外,还能被其他文件使用的变量。命名空间作用域中定义的变量(全局变量)默认都是外部变量,但在其他文件中如果需要使用这一变量,需要用extern关键字加以声明。

//源文件1
int i=3;

//源文件2
extern int i;//声明一个在其他文件中定义的外部变量i
void fun(){
    i++;
}

对外部变量的声明可以是定义性声明,即在声明的同时定义(分配内存、初始化),也可以是引用性声明(引用在别处定义的声明)。在命名空间作用域中,不用extern关键字声明的变量,都是定义性声明;用extern关键字声明的变量,如果同时指定了初值,则是定义性声明,否则是引用性声明。

外部函数

在所有类之外声明的函数,都是具有命名空间作用域的。


编译预处理

1、#include指令

2、#define和 #undef指令

用 #define来定义符号常量,如:

#define PI 3.1415926

用 #define定义空符号,如:

#define MYHEAD_H

定义它的目的,仅仅是表示“MYHEAD_H已经定义过”这样一种状态。

#undef的作用是删除由 #define定义的宏,是指不再起作用。

3、条件编译指令

可以限定程序中的某些内容要在满足一定条件的情况下才参与编译。

4、defined操作符

它是一个预处理操作符,而不是指令,因此不要以#开头。使用形式为:

defined(标识符)

若“标识符”在此前经 #define定义过,并且未经 #undef删除,则上述表达式为非0,否则为0。

你可能感兴趣的:(C++,c++)