320 likes | 487 Views
第 16 章 数据的共享和流通. 四、静态成员 五、指向成员的指针. 四、静态成员 extern 在不同的模块中沟通全局变量的外部连接 , static 则充当内部屏蔽作用。 C++ 语言中静态成员属于类内部的合法成员但凌驾于对 象之上。模块之间静态成员具有 extern 的外部连接属性。 a. 静态数据成员 static 变量具有特别惹人垂青的性能: 1). 生命与程序等长的持久性。 2). 维持其原先位置的隐蔽性,禁止外部直接访问。 静态全局变量是内部连接的,但静态成员变量具有外部 连接属性。.
E N D
第16章 数据的共享和流通 • 四、静态成员 • 五、指向成员的指针
四、静态成员 • extern在不同的模块中沟通全局变量的外部连接, • static则充当内部屏蔽作用。 • C++语言中静态成员属于类内部的合法成员但凌驾于对 • 象之上。模块之间静态成员具有extern的外部连接属性。 • a.静态数据成员 • static变量具有特别惹人垂青的性能: • 1). 生命与程序等长的持久性。 • 2). 维持其原先位置的隐蔽性,禁止外部直接访问。 • 静态全局变量是内部连接的,但静态成员变量具有外部 • 连接属性。
[例]静态成员变量的声明、定义和使用 • #include <stdio.h> • static long* pa; • class CType • { public: static int data; • CType (int n=1) { m_n=n; data++; } • static void f() • { pa=sa; } • protected: static long sa[ ]; • private: int m_n; • }; • 静态成员声明static long sa[ ]; 相当于原先的外部全局 • 变量连接说明extern long sa[ ];
静态成员变量的定义和初始化设置: • 1. 定义和初始化不管是公共的还是私有的一律采用相 • 同的格式。 • 2. 定义时不再加static关键字但类域分辨符则是静态成 • 员变量定义的整体部分。 • 3. 定义是唯一的且在文件范围内进行,定义时可以不赋 • 初始值,如同定义全局变量。 • 4. 最好在.cpp部分同时指定初始值,特别定义私有的静 • 态成员变量时。放在头文件的全局范围定义静态成员变量容 • 易导致定义的重复,因此避免这样做是上策。
5. 不完备的声明语句如[static long sa[];]在定义点需补 • 充足够的信息。 • 6. 在构造函数中对静态成员变量赋值不牵涉静态成员变 • 量的内存分配, 高度警惕对静态成员变量data=const合法的 • 简单赋值,这导致对象构造一次静态成员变量又回归原值。 • 下面是静态成员变量在文件范围定义的格式: • 数据类型 类名::静态成员变量名;
[例] • int CType::data; • long CType::sa [ ]={20,30,40}; • void main () • { CType::data=0; • printf ("%d,static data=%d\t", sizeof • (CType),CType::data); • CType::f (); • printf ("pa[0]=%d,pa[1]=%d, • pa[2]=%d\n",pa[0],pa[1],pa[2]); • } • //输出:4,static data=0 pa[0]=20,pa[1]=30,pa[2]=40
上面例题中并无具体对象,静态成员变量明显地独立于上面例题中并无具体对象,静态成员变量明显地独立于 • 对象而存在。可以当作全局变量一样在类外通过全限定名操 • 作公共的静态成员如CType::data,也可以通过该类的实例以 • 对象名obj.data方式进行处理。 • 对象名obj.data方式的访问与对象本身的具体属性毫不 • 相干。根据内存数据状态唯一性原则系统最终将其归约为 • CType::data的形式。 • 私有的保护的静态成员变量由成员函数和友元访问。 • 静态成员变量可以用于监控一组成员函数,纪录它们被 • 调用的次数,根据调用次数进行适当的恢复或前进操作。 • 静态成员变量实质上是平移到类中再加上访问控制规则 • 制约的全局变量。
b.静态成员函数 • 静态成员函数没有隐含的this指针,因而不操作类中的 • 非静态成员,因而不能是虚函数。只读成员函数本质上是限 • 定this指针为只读指针。 静态成员函数不含当前类的this指 • 针因而不能是只读成员函数。 • 静态成员函数操作的是静态成员变量、类中的梅举常数 • 和全局变量以及自身入口中的局部变量。 • 非静态的成员函数可以直接操作静态成员函数或全局函 • 数,静态成员函数需要通过对象或对象指针间接操作非静态 • 的成员。
下面是使用静态成员函数的三个步骤: • 1. 在类中通过关键字static声明类中的静态成员函数 • class CType • { protected: • static double function(int,float,...,long); • tatic 返回类型 函数名(形参列表); • }; • 2. 在类的外部给出静态成员函数的定义,定义时不再加 • static关键字但类域分辨符则是静态成员函数定义的整体部 • 分。 • 静态成员的连接属性根据语言约定是extern性质,但 • inline定义的静态成员函数是内部连接的。
返回类型 类名:: 函数名(形参列表) • {不含this的语句序列;} • double CType::function (int n,float f,...,long l) • {不含this的语句序列;} • 3.公共的静态成员函数可直接全限定方式调用,就象 • 调用全局函数: • double v=CType::function (n,f,..., l); • 对象名的访问方式也是可行的如: • CType a; double v= a.function (n,f,..., l); • 对象或对象指针调用静态成员函数,与对象的数据状态 • 无关。换言之a.function (n,f,..., l)是全限定访问形式的 • CType::function (n,f,..., l)的一个间接别名。
[例]静态成员对象实质上是全局对象, 静态成员函数实质上 • 是全局函数 • # include<stdio.h> • class A • { private: static A a; int m_n; • A(int n) • { m_n=n; printf ("call A::A ();\t"); } • public: static A& GetStatic (); • int GetNum () {return this->m_n; } • }; • inlineA& A::GetStatic () • {return a;} • A A::a(10);
void main() • { printf ("Here is in main scope\t"); • printf ("m_n=%d\t", A::GetStatic().GetNum()); • A& (*pf) ()=A::GetStatic; • printf ("m_n=%d\n",pf ().GetNum ()); • } • //输出: • call A::A(); Here is in main scope m_n=10 m_n=10
类中的声明中自身的对象是不许作为成员的。 • 上面A类中具有一个私有静态成员正好是A类的对象, • 但由于static修饰的结果这个对象实际上是一个加上类域分 • 辨符的全局对象,这个静态成员对象A::a是独立于类的具体 • 实例的,是唯一的。 • 静态成员函数实质上是加上类域分辨符和访问控制规则 • 制约的全局函数。 • 因此可以由类型属性为A& (*)()的常规函数指针pf间接 • 调用,而不是A& (A::*)()型的成员函数指针。
五、指向成员的指针 • 指针是用于管理内存数据的变量,指向成员的指针分两 • 种:一种是指向成员函数的指针,另一种是指向成员数据的 • 指针。用type,t1,t2等表示类名。 • type,t1,t2等可以是int,long,float等也可是结构名等, • 用CType表示类类型名 • 1.指向数据成员的指针 • 指向成员的指针是C++新引进的,C语言意义上的指针 • 称为常规指针或简称指针;指向成员的指针简称为成员指针.
静态数据成员是独立于对象的类的全局变量,因而指向静态数据成员是独立于对象的类的全局变量,因而指向 • 静态数据成员的指针是一个常规的数据指针。 • 定义指向数据成员的指针的格式为: • type CType::* pm; • 类名 类类型名::* 指向数据成员的指针 • 指向数据成员的指针pm的类型为type CType::*,而 • 常规指针的类型为type*,两者的差别在于类域分辨符 • CType:: • 如果CType类的声明里存在两个type型的成员变量, • 这个指向数据成员的指针pm可以指向其中之一。 • class CType { type m_t1; type m_t2; };
指向成员的指针可不在类的声明中,访问控制属性仅在指向成员的指针可不在类的声明中,访问控制属性仅在 • 该指针与其成员关联时起作用。 • 公共的成员可在当前类的外部如全局函数中赋给指向成 • 员的指针。 • 私有的或保护的成员可在成员函数或友元函数中与指向 • 成员的指针关联,而不在外部环境赋值。 • 一经关联编译器就难以限制指向成员的指针对成员访问 • 控制属性的挑战。 • 对于CType类特定的对象obj, type*型的指针p指向 • type型成员的格式是: • p=&obj. m_t1; • 在成员函数内部p的关联格式是: • p=&this->m_t1;
type*型的指针指向特定对象的数据成员,指向成员的 • 指针指向待定对象的相对偏移。 • 如下语句进行了指向数据成员的指针和类的成员变量的 • 关联: • 成员指针名=&类名::数据成员名 • pm=&CType::m_t2 • //此关联式中不含对象 C++引进两个运算符访问指向成员的指针,一个运算 • 符“.* ”是对象访问成员指针运算符,另一个运算符“->* ”是 • 对象指针访问成员指针运算符。 • 星号"*"右边是成员指针。 • 圆点"."左边是对象名,箭头"->"左边是对象指针。
下面是索引指向数据成员的指针的格式: • 对象.*成员指针 • obj.* pm • //对象obj访问成员指针pm 对象指针->*成员指针 • pObj->*pm • //对象指针pObj访问成员指针pm • this->*成员指针 • this->*pm • //this指针访问成员指针pm
对于类型为type CType::*的指针pm,表达式 • obj.*pm(或pObj->*pm, this->*pm) • 是type型的左值; • 如果type表示int,则obj.*pm是int型的左值,如果type • 表示double*型,则obj.*pm是double*型的左值。 • 对于对象a,b和对象指针p,q; • 当[pm=&CType::m_t2;]时,则[a.*pm;b.*pm;]等价于 • [a.m_t2; b.m_t2;] • 当[pm=&CType::m_t1;]时,则[p->*pm;q->*pm;]等价于 • [p->m_t1;q->m_t1;]
2.指向成员函数的指针 • 指向成员函数的指针简为成员函数指针。 • 成员函数指针是将常规函数指针平移到类内的产物。不 • 失一般性以两个参数为例,常规函数指针的定义格式为: • type (*pf)(t1, t2 ); • 类型 (*函数指针名)(类型1,类型2); • pf是具有类型属性为type (*)(t1,t2 )的函数指针。 • 在常规函数指针前冠上类域分辨符CType::就得到成员 • 函数指针的定义格式: • 类型 (类类型名::*成员函数指针名)(类型1,类型2); • type (CType::*pfm)(t1,t2 );
如上语句定义一个类型属性为 type (CType::*)(t1,t2 ) • 的普通又奇异的指针pfm。 • 成员函数指针可独立于对象而存在,但同时又必须指向 • 成员函数,成员函数具有访问控制属性,而超乎其外的成员 • 函数指针并不全然理会私有禁锢的制约。 • 在初始化成员函数指针时编译器设置一道防线维持内部 • 访问控制的约束机制: • 如果成员函数指针指向私有或保护的成员函数,初始化 • 可在友元或成员函数中进行。 • 如果成员函数指针指向公共的成员函数,初始化可在全 • 局范围或全局函数中进行。
指向静态成员函数的指针是常规的函数指针。 • 指向静态成员和变量如obj.m_t的常规指针和指向成员 • 的指针初始关联时存在相同的约束,在可访问的环境获得初 • 始值后, 常规指针和指向成员的指针就可以在外部实施对 • 成员的间接访问。 • 成员函数指针一经赋值,该指针则可以在外部实施对成 • 员函数的间接调用。 • 因此指针是解除内部封装的有力手段。
成员函数指针的初始化语法格式为: • 成员函数指针名=类名::同类型的成员函数名; • pfm= &CType::f; • 成员函数f具有与成员函数指针相同的类型属性即函数 • 原型的全限定形式为: • 返回类型 类名::函数名(类型1,类型2); • type CType::f (t1, t2 ); • 同全局函数一样取地址运算符&是可选的,亦可在定义成 • 员函数指针时就赋初值: • 类型 (类名::*成员函数指针)(类型1,类型2)= • 类名::同类型的成员函数名; • type (CType::*pfm)(t1, t2 )= &CType::f;
成员函数通过对象名或对象指针来调用,成员函数指针成员函数通过对象名或对象指针来调用,成员函数指针 • 的使用格式为: • (对象名.*成员函数指针)(实参1,实参2) • (obj.*pfm)(v1,v2) • (对象指针->*成员函数指针)(实参1,实参2) • (pobj->*pfm)(v1,v2) • (this->*成员函数指针)(实参1,实参2) • (this->*pfm)(v1,v2) • 当[pfm= &CType::f;]时,间接调用(obj.*pfm)(v1,v2) • 相当于直接调用obj.f(v1,v2)。实参v1,v2应与类型t1,t2匹 • 配。 • 返回类型确定函数调用的使用方式。 • 规定成员函数指针不指向构造函数和析构函数,即 • pfm=&CType::CType是非法的。
由于成员函数指针的类型书写起来过于冗长,因此指定由于成员函数指针的类型书写起来过于冗长,因此指定 • 一个成员函数指针的别名就是自然的事情,从定义成员函数 • 指针的格式前在冠一个typedef关键字就得到类型的别名: • type (CType::*pfm)(t1,t2); • typedef type (CType::*PFM)(t1,t2); • PFM是类型type (CType::*)(t1,t2)的一个别名。 • 由此可以定义成员函数指针或数组: PFM pfm, pfmn[5];
[例]指向数据成员的指针,静态成员函数的地址属性同等于[例]指向数据成员的指针,静态成员函数的地址属性同等于 • 全局函数的地址属性 • #include <stdio.h> • class CType • { public:static void Setpm (int CType::* & ,int k) • void Set (int CType::*& pm,int k) • { Setpm (pm,k); this->*pm+=k; } • CType (int x=10,int y=20) • { m_t1=x; m_t2=y; } • void Show() • { printf ("m_t1=%d,m_t2=%d\t",m_t1,m_t2); } • public: int m_t1; • private: int m_t2; • };
void CType::Setpm ( int CType::*& pm, int k) • { if (k==1) pm=&CType::m_t1; • else pm=&CType::m_t2; • } • void (*Setpm)( int CType::*& ,int)= CType::Setpm; • void main () • { int CType::* pm; • CType::Setpm(pm,1); CType a; • a.Show(); a.*pm+=10; • Setpm(pm,2); • a.*pm+=20; a.Set (pm,1); a.Show (); • } // 输出:m_t1=10,m_t2=20 m_t1=21,m_t2=40
[例]指向成员函数的指针 • #include <stdio.h> • class CType; • typedef int (CType::*PFM)(int); • class CType • { public: static void Setpfm (PFM & pfm,int); • CType (int x=0) { pfm(x); } • void Show() { printf ("m_x=%d\t",m_x); } • int pfm (int x) { m_x=x; return 2; } • private: int Add (int x){m_x+=x; return 1; } • int m_x; • };
void CType::Setpfm (PFM & pfm,int num) • { switch (num) • { case 1: pfm=&CType::Add; break • default: pfm=&CType::pfm; break; • } • } • int (CType::*gpfm) (int)=&CType::pfm;
void main () • { CType* pthis=new CType (); • (pthis->*gpfm)(10); pthis->Show (); • PFM pfm; • CType::Setpfm (pfm,1); • (pthis->*pfm)(20); • pthis->Show (); • CType::Setpfm (pfm,2); • (pthis->*pfm) (3); • pthis->Show (); • pthis->pfm (4); • pthis->Show (); • }
//输出:m_x=10 m_x=30 m_x=3, m_x=4 • 说明: • 上面的程序定义一个名为pfm的指向成员函数的局部指 • 针,在类中存在一个名为pfm的成员函数。对于成员函数指 • 针pfm的访问格式为: • (pthis->*pfm)(3) • pfm右边紧贴右圆括号左圆括号对“)(”是必不可少的。 • (pthis->*pfm)(3)是对于指向成员函数指针的间接调 • 用, pthis->pfm (4)是直接调用成员函数pfm。