第6章类的数据共享与保护
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
a.由于它是属于某个类(A),所以在类外定义 时必须加作用域运算符来限定它属于某一类(A)的, 在使用时必须加对象名和成员运算符来指定哪一 个对象调用它;
b.由于它是另一个类(B)的友元,所以在其参 数中必须用该类(B)的对象和引用作为形参。
c.在实现代码上,必须用该类(B)的对象名加成 员运算符来使用该类(B)的私有数据。
(3) 常对象只能调用它的常成员函数,而不能调 用其他成员函数。
见例5-7(P147-148)
2. 常数据成员
用const说明的数据成员称为常数据成员。常 数据成员的值在任何函数中都不能被更改,必须编 写构造函数并采用初始化列表的方式进行初始化。
见例5-8(P148)
6.4.1 常引用
函 数 的 参 数 可 以 被 说 明 为 常 引 用 ( 用 const 修 饰),则在该函数内不能修改该形参的值。
6.4 常对象和常成员(const)
虽然数据隐藏保证了数据的安全性,但各种 形式的数据共享却又不同程度地破坏了数据的安全。 因此,对于既需要共享、又需要防止改变的数据应 该声明为常量。因为常量在程序运行期间是不可改 变的,所以可以有效地保护数据。
本节介绍用const定义的常对象、常成员和常引 用。
友元分为两种情况:将一个普通函数声明为某 类的友元和将一个类声明为另一个类的友元。
6.3.1 友元函数
要声明一个普通函数为某类的友元, 只要在该类 定义中任何一处(不管是private、protected或是 public区)提供对该函数的声明,并在其最前方加上 关键字friend即可。下面是计算两点距离的实例:
见例(P145)。
友元说明只有在确实必要时才应使用,即在 没有它时必须建立一个复杂的类等级的时候才使用。 从性质上来说,友元是一种数据共享,它破坏了类 的封装性,因此要尽量少用。
在使用友元时还要注意:(1)友元不具有可传 递性,如B是A的一个友元,A是C的一个友元,并 不能得出B是C的友元的结论。(2)友元关系是单向 的, 如果B类是A类的友元,B类的成员函数可以访 问A类的私有数据,但A类的成员函数不能访问B 类的私有数据。(3)友元关系不能被继承,如果B类 是A类的友元,B类的派生类不能自动成为A类的 友元。
static int countP;
静态数据成员的使用与普通数据成员的使用 并无差异,但必须注意两点:
a.由于静态数据成员在该类范围内是一个全局 变量,因此必须在类定义外的全局范围中给它赋 初值,然后才能使用。如下所示:
int Point::countP=0;
给静态数据成员赋初值时必须同时指名数据类 型与所属的类,用类名加作用域运算符来限定。这 里的数据类型必须与在类中声明的数据类型一致。
第6章 类的数据共享与保护
本章主要内容
• 对象(变量)的作用域、可见性与生存期 • 类的静态成员 • 类的友元 • 常引用、常对象和常成员
6.1 对象(变量)的作用域、可见性和生存期
• 对象(变量)的作用域:指对象的有效范围。
- 一般情况下,对象在其作用域内是可见的和存在的,对象 的作用域、可见性和生存期三者是一致的。
class A; //一个类声明语句 class B {
friend A; //声明A为B的友元类 int i; void m_func( ); };
class A { public:
void f_f1(B &); void f_f2(B &); ...... };
对于友元类的成员函数的使用和定义方式, 由于它兼具有类和友元函数的双重特点,所以它 的使用和定义方式颇具特色,具体如下:
实际上,当某成员函数只使用静态数据成员时, 应该把该成员函数定义为静态成员函数。这是因为, 一个仅使用到静态数据成员的成员函数通过某类的 对象来调用是没有任何意义的,且容易造成阅读上 的混淆,阅读者无法从程序中直接看出该成员函数 引用了静态数据成员。
因此 A.getC( ); B.getC( );
由于静态数据成员并不特别属于某一个对象, 上述这样的赋初值语句只能在整个程序中出现一次。 因此,程序员最好不要把静态成员的初始化定义在 头文件中,因为头文件可能在程序中多次被引入使 用,否则将发生错误。
b.当静态数据成员声明于类的public区时,由于 它并不特别属于某一个对象, 在类范围内是全局的, 因此可以在程序的任何地方直接引用该静态数据成 员,但使用时必须加上类范围的限制,如:
改为: Point::getC( ); Point::getC( );
更为妥当。
见例5-5 (P140)。
另一方面, 静态成员函数不属于某个对象的, 因 此, 在静态成员函数访问非静态成员时,必须要指明 所要访问的对象。例如,若在getC中要访问非静态 成员X, 则getC必须修改为:
static void getC(Point &p) { cout<<"对象个数为:"<<countP<<endl;
下面对静态数据成员和静态成员函数分别进 行讨论。
6.2.1 静态数据成员
在一个类中,若将一个数据成员说明为static, 则该数据成员称为静态数据成员,无论建立多少个 该类的对象,都只有一个共同的数据成员。static数 据成员在编译时按全局变量方式被分配存储单元并 初始化,默认的初始值为零。
欲声明一个成员为静态的,只要在其前方加上 关键字static即可, 如在Point类定义一个静态数据成 员countP :
double fDist (Point &p1, Point &p2) {
double x=double(p1.X-p2.X); double y=double(p1.Y-p2.Y); return sqrt (x*x+y*y); }
见例5-6 (P143)
请注意友元函数与成员函数在定义和使用上的 区别。成员函数与友元函数的主要区别是成员函数 是属于某个类的,而友元函数不属于某个类。
类的所有对象在调用这个成员函数时将共享这个 变量。 • C++类的成员为静态成员:静态成员为该类所有对 象所共享,它不属于某个对象的,见下节。 • 程序实例
6.2 静态成员(static)
在C语言中,当把函数的一个局部变量说明为 静态的,则该变量在函数调用结束后其值仍然存在, 但其它函数不能引用。若下次再调用该函数,则该 变量的值不再重新初始化为0,函数可以使用上次 调用所保留的值。
cout<<"对象个数为:"<<Point::countP;
下面我们来看看例5-4 (P138)。
由于静态数据成员是所有同类对象所共用的数 据,所以
A.getC(); B.getC(); 这两个输出语句的结果是相同的,即:
对象个数为:2 对象个数为:2
从程序的最后两句可以看出,类的public数据 成员有两种使用方法:
作用域关系图
6.1.3 对象(变量)的生存期
• 对象(变量)的生存期:动态生存期、静态生存期。 • 动态生存期:对象(变量)在程序运行期间随时诞生
和消失,如局部变量、形参变量、成员变量。 • 静态生存期:对象(变量)在程序运行期间一直存在,
如全局变量。
• C语言中静态局部变量:作用域与生存期不一致。 • C++在类的成员函数中定义一个静态局部变量:该
对象名.数据成员
或
类名::数据成员
第一种方式主要用于非静态数据成员,第二
种主要用于静态数据成员。
6.2.2 静态成员函数
成员函数也可以被声明为静态的。当一个成 员函数被声明为static时,表明该静态成员函数只 属于一个类,而不属于该类的任何对象。因此, 在访问静态成员函数时,最好用类名加作用域运 算符来调用该静态成员函数。
class Point { int X,Y;
friend float fdist(Point &p1, Point &p2);//友元函数 public:
Point (int xx=0, int yy=0){X=xx; Y=yy;} };
与成员函数一样,友元函数可以在类Point的 内部定义,也可以在类Point的外部定义。当在类 外定义时, 友元函数不需要加“类::”。如上述fDist 函数在类Point外定义:
• 局部对象(变量):(函数内)块作用域
- 从声明处开始到块结束的花括号为止
• 形参对象(变量):函数作用域
- 在整个函数内有效
• 成员对象(变量):类作用域
- 在整个类内有效,即在所有成员函数内有效
• 全局对象(变量):文件作用域
- 在整个文件内有效
6.1.2 对象(变量)的可见性
• 一般情况下对象(变量)在其作用域范围内的任一处是 可见的,即可被引用。
在友元函数中,必须指定所要访问的对象,这 在三个地方体现出来:
① 在参数中有2个Point类的引用; ② 在实现代码中通过引用对象p1和“.”来使用数据
成员; ③ 使用友元函数(即调用该友元函数)时必须有该类
对象作为实参。
6.2.2 友元类
我们可以将类A声明为类B的友元,这样类A的 所有成员函数就可以访问类B的私有数据。例如:
1. 常成员函数
使用const关键字说明的函数为常成员函数, 常成员函数据说明格式如下:
类型说明符 函数名(参数表)const;
注意: (1) const是函数类型的一个组成部分,因此在
实现部分也要带const关键字, 并且可用于对重载函 数的区分。
(2) 常成员函数不更新对象的数据成员,也不能 调用该类中没有用const修饰的成员函数。
• 在有不同作用域的多个同名对象(变量)的情况下,外 层对象(变量)在内层不可见, 被隐藏起来。
• 不同作用域的同名对象的 包含关系如右图所示。
• 被隐藏的成员对象(变量) 可加“类名::”来显式地引用, 被隐藏的全局对象(变量) 可加“::”来显式地引用。
文件作用域 类作用域 函数作用域 块作用域
见例5-9(P150)
本章小结
• 一般情况下, 对象在其作用域内是可见的和存在的, 对象的作用域、可见性和生存期三者是一致的, 但在同名和加了static后会出现不一致的情况。
• 静态成员是类中所有对象共有的,不属于某一对 象的。
• 友元函数和友元类可以访问类的私有成员,破坏 了封装性,应慎用。
• 对象(变量)的可见性:指不同作用域的同名对象 在其作用域范围内的某一处是否可被引用。
• 对象(变量)的生存期:指对象从诞生(占据内存) 到结束(不占内存)的这段时间。在生存期内, 对象 或变量将保持它的值不变, 直到它们被更新为止。
-对象(变量)的生存期分为动态象(变量)的作用域
6.4.1 常对象
常对象是指常类型对象,即对象常量,定义的 语法形式为:
类名 const 对象名(初值); 或
const 类名 对象名(初值);
定义常对象同样要赋初值, 并且该对象不得再更新。 另外,常对象只能调用它的常成员函数,而不能调 用其他成员函数。参见下面的常成员函数。
6.4.3 常成员
在C++中,对于某个类所定义的每一个对象, 都有其属于自身的数据成员与成员函数,不同对象 之间的成员是互不相干的,这类似于C中函数内的 局部变量,不同时间函数调用的局部变量是互不相 干的。但它们都同名。
因此,在C++中,当把类的某个成员用关键 字static说明为静态成员时, 就是把该成员定义为 在该类范围内的全局成员,即无论这个类建立了 多少个对象,所有对象都共用这个成员。因此, 静态成员的主要用途是定义类的各个对象所共用 的数据成员或成员函数,其中尤其是数据成员。
cout<<“X:”<<X<<endl; //错误
cout<<“X:"<<p.X<<endl;
}
则main函数中的调用语句改为:
Point::getC(A); Point::getC(B);
见修改后的例5-5 析构函数有代码
6.3 友元(friend)
前面已讲过, 一个对象的私有数据只能通过成员 函数进行访问。这种数据封装的方法虽有许多优点, 但在某些情况下也带来许多不便, 如某个函数需要使 用多个类的多个相关数据, 或类Y的所有成员函数要 访问类X的私有数据。出于效率而非技术的考虑, C++提供了友元(friend)这样一种辅助手段, 允许外面 的类或函数去访问一个类的私有数据。