}
5_2实现客户机(CLIENT)类。
声明字符型静态数据成员ServerName,保存其服务器名称;声明整型静态数据成员ClientNum,记录已定义的客户数量;定义静态函数ChangeServerName()改变服务器名称。
在头文件中声明类,在文件中实现,在文件中测试这个类,观察相应的成员变量取值的变化情况。
三、实验步骤
1.(验证)运行程序,观察程序输出。
全局变量的作用域为文件作用域,在整个程序运行期间有效,但如果在局部模块中声明了同名的变量,则在局部模块中,可见的是局部变量,此时,全局变量不可见;
而局部变量的生存期只限于相应的程序模块中,离开相应的程序模块,局部变量x、y就不再存在,此时同名的全局变量重新可见。
程序名:
。
★程序的运行结果:
2.(编程)实现客户机(CLIENT)类。
新建一个空的项目lab5_2,添加头文件,在其中声明类CLIENT,注意使用编译预处理命令;再添加源程序文件,在其中实现CLIENT类,注意静态成员变量的使用方法;再添加文件,在其中定义main()函数,测试CLIENT类,观察相应的成员变量取值的变化情况。
提示:
访问一台服务器的客户总数。
静态成员为类的属性,为所有的类的对象共同拥有。
再定义两个静态成员函数,分别显示服务器名和客户总数。
构造函数用于增加一个客户,析构函数用于减少一个客户。
定义一个对象,再定义第二个对象,然后减少一个对象。
参考程序输出结果:
★程序及运行结果:
(1)类声明头文件
showServerName();showClientNum();
{
clientb;showServerName();
b.showClientNum();
}
5.1.1函数原型作用域
在函数原型声明时形式参数的作用范围。
例,
doubleArea(doubleradius);
其中标识符radius的作用(或称有效)范围在形参列表的左、右括号之间,称标识符radius的作用域是函数原型作用域。
由于在函数原型的形参表中起作用的只是形参类型,标识符并不起作用,可省略。
2.局部作用域(块作用域)
例:
形参作用域:
从形参列表中的声明处开始,到整个函数体结束处止。
b和c都具有块作用域,是不同的块作用域。
块是一对大括号括起来的一段程序。
此例,函数体是一个块,if语句后的分支体又是一个较小的块,二者是包含关系。
在块中声明的标识符,其作用域从声明处开始,一直到块结束的大括号为止。
具有块作用域的变量也称为局部变量。
3.类作用域
类是一组有名成员的集合,类X的成员m具有类作用域,对m的访问方式如下:
(1)若在X的成员函数中无同名的局部作用域标识符,则在该函数内可访问成员m。
(2)通过表达式或X:
:
m。
这正是程序中访问对象成员的最基本方法。
(3)通过prt->m这样的表达式,其中prt为指向X类的一个对象的指针。
4.命名空间作用域
命名空间
大型程序通常由不同模块构成,不同模块中的类和函数之间可能发生重名,将引发错误。
命名空间可以解决类名、函数等的命名冲突。
命名空间语法形式:
namespace命名空间名{
命名空间内的各种声明(函数声明、类声明、……)
}
例
namespaceSomeNs{
classSomeClass{...};
}
特殊的命名空间
_全局命名空间:
默认的命名空间
_匿名命名空间:
对每个源文件是唯一的
命名空间作用域
一个命名空间确定了一个命名空间作用域
引用其它命名空间作用域中的标识符
_命名空间名:
:
标识符名
_例:
声明一个SomeClass型的对象
SomeNs:
:
SomeClassobj1;
将其它命名空间作用域的标识符暴露于当前作用域
_对指定标识符
using命名空间名:
:
标识符名;
_对所有标识符
usingnamespace命名空间名;
例5-1中所声明的全局变量就具有文件作用域,它们在整个文件中都有效。
例5-1作用域实例。
#include
usingnamespacestd;5.1.25.2.15.2.2.
}
若需要统计雇员总数,这个数据存放在什么地方呢?
若以类外的变量来存储总数,不能实现数据的隐藏。
若在类中增加一个数据成员用以存放总数,必然在每一个对象中都存储一个副本,不仅冗余,且每个对象分别维护一个“总数”,势必造成数据的不一致性。
比较理想的方案是类的所有对象共同拥有一个用于存放总数的数据成员。
5.3.1静态数据成员P154
实例属性
“一个类的所有对象具有相同的属性”,是指属性的个数、名称、数据类型相同,各个对象的属性值则可各不相同。
以类的非静态数据成员表示。
类属性
是描述类的所有对象的共同特征的一个数据项,对于任何对象实例,它的属性值是相同的。
通过静态数据成员来实现“类属性”。
静态数据成员的访问
静态数据成员不属于任何一个对象,只能通过类名对它访问,用法是“类名:
:
标识符”。
静态数据成员的说明和定义
在类的声明中仅仅对静态数据成员进行引用性说明,必须在文件作用域的某处用类名限定进行定义性说明,这时也可进行初始化。
在UML中,静态数据成员下方添加下划线。
例5-4具有静态数据成员的Point类。
引入静态数据成员的Point类。
图5-2包含静态数据成员的Point类的UML图
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
usingnamespacestd;
classPoint5.3.2
5.4.1
5.4.2..
};
声明友元类,是建立类与类之间的联系,实现类之间数据共享的一种途径。
在UML中,两个类之间的友元关系是通过<>构造型依赖来表征。
图5-5类A和类B友元关系的UML图
#include
usingnamespacestd;
classA{
public:
voiddisplay(){cout<intgetx(){returnx;}
friendclassB;
private:
intx;
};
classB{
public:
voidset(inti);
voiddisplay(){cout<<<private:
Aa;
};
voidB:
:
set(inti){
=i;5.5.15.5.2常成员函数
声明格式:
类型说明符函数(参数表)const;
注意:
①const是函数类型的一个组成部分。
②常成员函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。
③常对象只能调用它的常成员函数。
④const可用于对重载函数的区分。
例如,
voidprint();
voidprint()const;
这是对print的有效重载。
在UML中,常成员函数前添加<>。
例5-7常成员函数举例。
图5-6包含常成员函数的R类的UML图
R
–r1:
int
–r2:
int
+R(rr1:
int,rr2:
int)
+print():
void
<>+print():
void
#include
usingnamespacestd;
classR{
public:
R(intrr1,intrr2):
r1(rr1),r2(rr2){}
voidprint();
voidprint()const;常数据成员
类的成员数据也可以是常量。
使用const说明的数据成员为常数据成员。
任何函数中都不能对常数据成员赋值。
构造函数对常数据成员进行初始化,就只能通过初始化列表。
在UML中,常数据成员前添加const。
例5-8常数据成员举例。
图5-7包含常数据成员的A类的UML图
A
–a:
constint
–b:
constint=10
+A(i:
int)
+print():
void
#include
usingnamespacestd;
classA{
public:
A(inti);
voidprint();
private:
constinta;5.5.35.6.1C文件)
类实现文件(*.cpp文件)
类的使用文件(*.cpp,主函数文件)
每个源程序文件称为一个编译单元。
C++语法要求一个类的声明必须出现在所有使用该类的编译单元中。
惯用的做法
将类的声明写在头文件中,使用该类的编译单元则包含这个头文件。
例5-10具有静态数据、函数成员的Point类,多文件组织。
-8C文件,也可以是.cpp文件。
#include两上书写方式
#include<文件名>,按照标准方式搜索要嵌入的文件,该文件位于C++系统目录的include子目录下,一般包含系统提供的标准文件时采用这样的方式。
#include"文件名",首先在当前目录下搜索要嵌入的文件,若没有,再按照标准方式搜索,对用户自己编写的文件一般采用这种方式。
在使用多文件结构时,注意内联函数的特殊性
一个内联函数,需要在每个调用它的编译单元中给出一个完全一致的实现。
惯用的做法是将内联函数的实现写在头文件中,由调用的编译单元包含这个头文件。
5.6.2外部变量与外部函数P170
用extern声明外部变量。
外部变量是具有文件作用域的变量,定义在所有文件之外。
声明一个外部变量时,可以同时定义它,也可以只是引用一个在别处声明的外部变量。
外部函数
非成员函数具有文件作用域,可以在不同的编译单元被调用,只要在调用之前声明函数原型即可。
可以在声明函数原型或定义函