c++类中的各种成员-嵌套类、友元和this指针

本文作者:黄邦勇帅
学习本文首先你应熟悉C++中的构造函数,基本的类的声明及怎样初始化类,关于这些问题,请参看本人所作的《C++构造函数,复制构造函数和析构函数》一文,在这篇文章中作了详细的介绍。掌握C++类中的各种成员,是学习好C++的基础,因此对于本文的内容应全部熟练掌握,本文主要集中介绍C++类中的各种成员,这些成员分别是:类中的静态(static)成员变量,成员函数;const(常量)成员变量,成员函数和const 对象;const、static(常量静态)数据成员;对象数组;类中的对象成员;类成员指针;嵌套类;友元;this 指针以及.*和->*运算符共12种内容。本文内容全面,简单易懂,是学习C++不错的选择。本文内容完全属于个人见解与参考文现的作者无关,其中难免有误解之处,望指出更正。
声明:禁止抄袭本文,若需要转载本文请注明转载的网址,或者注明转载自“黄邦勇帅”。
主要参考文献:
1、C++.Primer.Plus.第五版.中文版[美]Stephen Prata 著孙建春韦强译人民邮电出版社2005 年5 月
2、C++.Primer.Plus.第四版.中文版Stanley B.Lippman、Barbara E.Moo 著李师贤等译人民邮电出版社2006 年3 月
3、C++.Primer.Plus.第三版.中文版Stanley B.Lippman 等著潘爱民张丽译中国电力出版社2002 年5 月
4、C++入门经典第三版[美]Ivor Horton 著李予敏译清华大学出版社2006 年1 月
5、C++参考大全第四版[美]Herbert Schidt 著周志荣朱德芳于秀山等译电子工业出版社2003 年9 月
6、21 天学通第四版C++ [美]Jesse Liberty 著康博创作室译人民邮电出版社2002 年3 月

类中的各种成员
const ,static,[],*p,const static,对象成员,常量对象,mutable

1.类中的静态成员变量static:被类中的所有对象所共享,静态成员属于整个类不属于某个对象。静态成员变量和全局变量差不多,只是他的作用范围为定义他的类及类的对象所知。


1.1.当在一个类中声明静态成员变量时并没有定义该变量即没有为他分配存储空间,所以必须在类外对静态成员变量提供全局定义,注意必须在类外,这样就可以为变量分配内存,定义的方式为使用作用域解析符,比如有类hyong 类中有静态整型成员a,那么在类外定义该变量的方式为int hyong::a=9;注意必须要有在类中声明的类型,如果定义时没有初始化则自动初始化为0。


1.2.静态成员变量如果是类中的公有成员则可以在程序中使用语句hyong::a 来访问该变量,当然也可以通过对象名来访问该变量,如果变量是私有或者保护的,则只能用静态的成员函数来访问该变量。


1.3 不能用初始化列表来初始化静态成员变量。


2.静态成员函数static,静态成员函数没有this 指针,静态成员函数只能引用这个类的其他类静态成员,当然全局函数和数据可以访问,因为类的函数都要用this 指针来访问成员,因为静态成员函数没有this 指针,所以不能访问除静态成员之外的成员。同一个函数不可以有静态和非静态两种版本,静态成员函数不可以是虚函数,静态成员函数不能是const 和volatile。静态成员函数使用是有限的,通常使用静态成员函数来访问类中的私有静态成员变量。在类外定义的形式为int hyong::g(){}注意,这里没有static 说明符。


3.const成员变量参看第四节初始化列表。


4.const成员函数,形式为int f() const{}注意const 在括号后,如果把const 放在int 前就成为一个反回const int 类型的函数了,把函数声明为const 后就使得this 可以当做一个const 指针,从而使得函数不能修改调用它的对象也就是说不能改变任何一个类的成员变量的值。如果想让const 函数能修改某一部分成员,可以把该成员声明为mutable 类型,例如mutable int a。在类外定义的形式为int hyong::f() const{}


5.const static 常量静态数据成员,这种类型的成员变量可以直接在类的定义中直接初始化,这也是唯一一种可以在类中初始化的类型,如果没有在类中初始化,在类外的初始化方式为const int hyong::a=2;注意const 和类型都必须有。


6.const常量对象:即把对象声明为常量,即const hyong m,常量对象不能调用可能改变对象的值的函数,因此常量对象只能调用类中的const 常量函数,因为不是const 的函数都有可能改变对象的值。


6.2.常量对象可以调用类中的公有成员,如m.a 就是正确的如果a 是公有的。


6.4.不能对常量对象的公有成员重新赋值,如m.a=3 就是错误的。但可以对类中的公有静态成员变量重新赋值,因为静态成员变是不属于这个常量对象,他是属于整个类的。


7.对象数组:对象数组即数组中的每个成员都是一个对象,例如hyong a[3];其中a[0],a[1],a[2]都是一个hyong 类型的对象。对象数组的初始化,如果有默认构造函数则语句hyong a[3]将调用默认构造函数初始化3 个对象,如果对象数组带有一个参数的构造函数则可以这样初始化hyong a[3]={1,2,3},如果对象数组带有多个参数的构造函数,则初始化方法为hyong a[3]={hyong(1,2),hyong(3,4),hyong(4,5)}。

例:各种类成员,对象数组,常量对象的使用。
class hyong 
{  static int e; //私有静态成员变量,声明静态成员变量时没有为他分配存储空间必须在类外部定义。
public: int a;
static int b; //静态成员变量
const static int c=9; //常量静态成员变量,只有这种类型的变量才能在类中声明时初始化,也可以在类外部初始化。
static int g(); //静态成员函数,一般用这个函数访问类中的私有静态成员变量,其他地方用处不大。
int f() const;  //常量成员函数,注意const的位置。
hyong(){a=b=e=4;} //注意,如果在类外部定义了静态成员变量,则可以在构造函数中重新设置他们的值。否则就会错误。
hyong(int i){a=b=e=i;}      hyong(int i,int j){a=i;b=e=j;}      void ff(){cout<<a;}   };
//static静态成员变量定义的注意事项
//hyong::hyong():a(1),b(1),e(1){}  //错误,不能用初始化列表初始化静态成员变量。
int hyong::b=8;     int hyong::e=7;
//定义静态成员变量,静态成员变量必须在内外定义,以便分配存储空间,如果不提供初值将被初始化为0;
//const int hyong::c=9; //静态常量成员变量也可以在类外初始化,注意其用法,没有static关见字。
//static静态成员函数定义的注意事项
int hyong::g() {cout<<e<<"\n";return e;
//cout<<a; a=1; //错误,静态成员函数只能访问类中的静态成员变量,既使不改变变量的值也是不行的,静态成员函数一般用来访问类
中的私有静态成员变量}
//常量函数定义的注意事项

int hyong::f() const {cout<<a<<b<<c<<e<<"\n"; 

return a;//注意除了静态成员函数外其他函数同样可以访问类中的私有静态成员变量。

//a=1;   //错误,const常量成员函数不能改变任何成员变量的值。}
int main()
{//static静态成员函数和变量的应用
hyong m;  cout<<m.a<<m.b<<m.c<<"\n";
// cout<<m.e;   //错误,静态成员变量e是私有的,对象m不能访问,可以通过静态成员函数来访问e,当然也可以用其他成员函数来访问e。
cout<<m.g()<<"\n";  //调用静态成员函数访问类中的私有静态成员变量e
//cout<<hyong::e;  //错误,静态成员变量e是私有的,只能通过函数来访问
cout<<hyong::b<<"\n";  //因为静态成员变量是属于整个类的,而不属于类的某个对象,所以可以通过作用域解析符来直接访问。
hyong n; cout<<n.b<<"\n";
m.b=5;   cout<<n.b<<m.b<<"\n";//输出两个五,注意这里用对象m改变静态成员变量b的值,但对于对象n来说对象n并没有改变他的值,然而b的值却改变了,这就说明静态成员变量属于整个类,不属于某个对象。
//对象数组的应用
hyong m=>=?@A//声明一个常量数组,这里将调用两次默认构造函数以初始化两个对象mR>S?TU=>V?
cout<<mW>S?XYZZU=>V?X[ZZ"\n"; //输出两个四,m]>S?TU=>V?被默认初始化函数初始化了。
hyong m_>=?`aVT=b@A//调用带有一个形参的构造函数初始化对象数组的成员
cout<<mg>S?XYZZU_>V?XYZZ"\n"; //输出一和二。
hyong m4j=?`aklmnop_TqrTklmnopsTtrb@A//调用带两个形参的构造函数,初始化对象数组的形式。
cout<<m4xS?XYZZUqyV?XYZZ"\n"; //输出三和五。
//const常量对象的应用
const hyong m1;//声明一个常量对象,常量对象不能调用可能改变对象值的任何函数。m1调用默认构造函数初始化他,一但被初始化常量对象的值将不能被改变。
m1.f(); //调用m1的常量成员函数,输出四四九四。
//m1.ff(); //错误,m1是常量对象,不能调用除了const之外的任何成员函数,因为他们都有可能改变常量对象m1的值。
//m1.a=ˆ@AA‰‰错误,常量对象不能改变成员变量的值。
m1.b=Š@A//正确,常量对象虽然不能改变成员变量的值,但静态成员变量是个例外,因为他不属于对象m1,他属于整个类,不属于某个对象。
cout<<m1.b<<"\n";}
8.类中的对象成员:即把对象作为另一个类的成员。比如class hyong1{public: hyong x;},这时如果声明了一个hyong1
的对象则调用hyong的默认构造函数初始化对象x,而不管hyong1的构造函数有没有初始化对象x,如果没有声
明hyong1的对象,则不会初始化对象x。
2.如果要在用带有参数的构造函数初始化类中的对象成员,则对象成员必须在初始化列表中初始化。否则将发生错
误。
3.如果在初始化列表中显式对hyong1的对象成员x初始化了,则用初始化列表的构造函数初始化对象x。不会再调
用对象成员x的默认构造函数初始化对象x了。
4.如果再在hyong1 的构造函数里对x 重新赋值,即有语句x=hyong(),此语句不是对对象变量x 初始化,而是对对
象x重新赋值,将调用赋值操作符函数。
例:类中的对象成员的注意事项
class hyong 
{ public: int a,b;   hyong(){a=b=0;cout<<"mogou"<<"\n";}    hyong(int i){a=b=i;cout<<"onegou"<<"\n";}
hyong(int i,int j){a=i;b=j; cout<<"t“mom”•<<"\n";}  };
class hyong1
{public: int a,b;
hyong –@AAA//把另一个类对象做为类的成员。且调用hyong的默认构造函数初始化对象
//hyong y(9); //错误,不能这样调用hyong的带一个参数的构造函数初始化类对象y;要调用带参数的构造函数初始化类对象成员只能在初
始化列表中进行。
hyong1(){a=b=¢@£m”¤ZZ"mogou1"<<"\n";}    hyong1(int i){a=b=i; cout<<"onegou1"<<"\n";}   hyong1(int i,int j);};
//如果要使用带参数的构造函数初始化类中的对象成员,只能在初始化列表中进行
hyong1::hyong1(int i,int j):a(i),b(j),©p_rabA//用初始化列表重新初始化类对象成员ª,此时就不会调用hyong的默认构造函数初始化
类中的对象°了。
int main()
{ hyong m; //输出mogou,此时没有创造hyong1的对象,所以不会初始化hyong1中的类对象³。
hyong1 n; //此时调用hyong的默认构造函数以初始化hyong1的类对象¶。
hyong1 n1(4,5); //此时用n1的带两个参数的构造函数初始化对象n1,并调用初始化列表的带有一个参数的hyong的构造函数初始化hyong1
类的成员变量¼,注意,此时不会调用hyong的默认构造函数初始化对象成员¿
n.À`klmnoptTÁr@A//对类中的成员对象Â重新初始化,这里是赋值将会调用默认赋值操作符,不会调用默认复制构造函数。
cout<<n.ËXYZZnX–X[@Ab//访问类中的对象成员的成员的方法。
9.类成员指针和.*,->*运算符
9.1.声明类成员指针的方式为:int hyong::*p1声明了一个指向类中整型成员的指针p1。int (hyong::*p2)()注意括号,声
明一个指向反回类型为int的无参数的函数的指针p2
9.2.对类成员指针的初始化方式为:p1=&hyong::a,p2=&hyong::f,注意初始化指向类函数的指针时不能省掉&地址运算
符。
.9.3..*运算符:.*运算符的左侧必须是一个类的对象,而右侧则必须是类类型的成员指针。比如hyong m;则运用方式为
m.*p1
9.4. ->*运算符:运算符的左侧必须是一个指向对象的指针,而右侧则必须是一个类类型的成员指针。比如有hyong
*p=&m,则运用方式为p->*p1。
9.5.类成员指针即指向类中成员的指针注意是直接指向类中的成员而不是指向对象的某一成员的指针,即与指针p=&m.a
是不一样的。类成员指针提供的是成员在类中的对象的偏移量,不是一个真正的指针。因为不是一个真正的指针所
以不能通过指针来访问类中的成员,而只能通过特殊的运算符.*或->*来访问指针指向的成员。比如*p1=2 ,
hyong::*p1=2是错误的,不能对类成员指针指向的类成员直接赋值。cout<<*p1<<hyong::*p1也是错误的,不能直接
用类成员指针来访问类中的成员。
例:类成员指针和.*与->*运算符
class hyong 
{public: int a,b;   int f(){cout<<"fhanshu"<<a<<"\n";return a;}    hyong(){a=1,b=Ì@bAAAklmnopint i){a=5,b=i;}  };
int main()
{ int ÍÎ@AAA//声明一个常量指针
int hyong::ÑÎV@A//声明一个类成员指针,注意这种指针是指向类中的成员的,不是指向对象中的某个成员变量
int (hyong::×Î=rpr@A//声明一个类成员指针函数
hyong ÙÎ_@AA//声明一个指向对象的指针
hyong m;   p=ÛUXY@A£m”¤ZZÍÎZZ"\n";  //注意指针p是指向的对象中的某个成员变量。
p1=ÞklmnoßßY@AAAAAAAAAAAA//注意赋地址的格式,注意指针p1是指向的类中的某个成员,和指针p是不同的。
pç`Ûklmnoßßè@AAAAAAAAAAA//指针pê指向类中的某个函数,注意语句格式,虽然函数名就是函数的地址,但这里必须得有õ地址运算符。
cout<<m.ùÎVZZUXYZZ"\n"; //用.ú运算符访问对象中的成员变量,.þ运算符的左侧必须是类类型的对象,右侧必须是该类型的成员指针。其
实m.456和m.a都是指的对象m中的成员a。只是m.956是通过指针来访问的。
//hyong::>56?@AB456?@ABCC错误,虽然p1是指向类成员变量a的指针,但不能直接使用该指针对该指针指向的成员变量进行访问,只能使用.T
和VW4运算符才能访问类中的成员变量。
//cout<<hyong::[56ABB\]^_``456ABBCC错误,和以上情况相同,只能通过.h或者kl4运算符来访问类成员变量。
m.n56?oAB\]^_``pq456``rstuA //把类成员指针指向的类的成员变量a的值赋x,并输出其值。
cout<<(m.~5o€``pq€``"\n"; //访问成员函数
p‚?ƒpABBBB\]^_``5@„…456``€5@†‡45o€``"\n"; //用ˆ‰4运算符访问对象中的成员,‹Œ4运算符左侧必须是类类型的指针,右侧必须是该类
类型的成员指针。
cout<<p‘’“``€5@”•€``"\n";}
10.this指针:this指针是所有成员函数的隐含指针,每次调用成员函数时,this指针就指向调用此函数的对象。可以在
成员函数类部使用显使用this 指针。友元函数不是类的成员函数,所以友元函数没有this 指针。静态成员函数也没
有this指针。this指针默认是* const this类型,即this 是一个常量指针,不能改变this指针指向的地址。
例:this指针的使用
class hyong     {public: int a,b;      hyong(){a=1,b=–A—BBBBBB˜™]sš€int i){a=b=i;}
hyong f(hyong ›œif (thisžŸ“`œq“Breturn  ABelse return ¡this;}
//this=¢œACC错误,this指针是常量指针,不能改变this指针指向的地址。}};
int main()    { hyong m(¨©s€@©uABBBBu?pq€sABBBBB\]^_``uq“``uqªABBB—
例:对各种与类有关的指针的综合使用,整个程序有12个输出语句,总共输出12个2。
class hyong  {public: int a,b,«\ABB˜™]sš€“?6©ª?oA—BBBB˜™]sš€int i){a=b=i;c=¬“A——A
//记住一条定律:点运算符的左边必须是对象,而箭头->运算符的左边必须是一个指针。
int main()
{ hyong m(­©s€@©45?ƒpABBBBBint ®5¯?ƒpq“ABBBBint hyong::°5u?ƒ˜™]sš±±“A
//指向对象的指针p的调用方法
cout<<p²³“ABBB//用´µ箭头运算符调用成员变量a。
cout<<(¹5q“ABB//用.运算符调用成员变量a。点运算符要求左边必须是对象,而¿5就代表指针指向的对象。
//调用类中的指针变量的方法
cout<<€pq\``4pq\ABB//注意调用方法,m.c表示调用对象m中的指针成员c的地址,加上Ç则表示调用对象m中的指针成员c的值。点运算符
的优先级高于Ð指针运算符,所以可用也可不用括号,点运算符的左边是对象m
//cout<<Õ5q“``4€5q“ABCC错误,因为p是指向对象的指针,点的运算符高于Ø运算符,所以点运算符的左边是一个指针p不是对象而发生错误。
//指向对象中的成员的指针的用法
cout<<á5¯ABB//pr只是一个整型指针,他指向的是对象中的成员变量a的地址。
//cout<<(å5¯q“``45¯q“``4€5¯q“``5¯q“;  //错误,pr不是一个对象,他只是一个指向对象中的成员变量的整型指针
//使用指向对象的指针调用对象中的指针成员的方法
cout<<è€45q\``4€€45q\AB//注意点运算符优先级高于ê运算符,ì5必须括起来,ï5表示的是指针p所指向的对象,(ò5q\就表示调用p指向
对象的指针成员c的地十,所以最后要用ù来表示指针c的值,即ü€€~5q\就表示p所指向的对象中的指针成员变量c的值。
cout<< !"#$%% &!'($)*++//使用简头运算符调用对象中的指针成员c的方法,因为;<运算符要求左边必须是指针,所以pEF$就表示调用指针
p所指向的指针成员c的地址,K&!LM$)就表示调调用的指针p所指向的对象的指针成员变量c的值。
//类类型的指针的调用方法
cout<<m.R!S*++//.T运算符左侧必须要是对象名,右侧必须要是一个类类型的指针
cout<<p]^ !S*++//_` 运算符左侧必须要求是一个指针,右侧也必须要求是一个类类型的指针
cout<<(d!)e !S*f+// 因为.g左侧必须要求是一个对象名,所以(i!)表示的就是指针p所指向的对象。
11、嵌套类:即类中的类。
11.1、嵌套的类的声明:比如class A{public: class B;}即在类A中声明了一个类B。
11.2、嵌套类和外围类是两个互相独立的类:也就是说嵌套类中的成员属于嵌套类外围类不知道这个成员,外围类的成
员属于外围类而嵌套类不知道外围类的成员。如果要在嵌套类中访问外围类的成员则必须以外围类的指针,引用
或对象的形式访问外围类中的成员。同样,如果要在外围类中访问嵌套类中的成员时也必须以嵌套类的指针,引
用或对象的形式来访问嵌套类中的成员。比如class A{public: int a; class B{public:int b; void gb(){a=3}};};就是错误
的,因为外围类的成员在嵌套类中是未知的,正确的方法为class A{public:int a;class B{public:int b; void gb(){A
m;m.a=3}};};即在嵌套类中声明一个对象m再用这个对象对访问外围类的成员。
11.3、嵌套的类的定义:嵌套的类即可以在外围类中定义也可以在外围类外定义,比如class A{public: class B{};};即在
外围类A中定义了一个什么也不做的嵌套的类B。而要在外围类A的外面定义嵌套类B的方法为:class A::B{};
在外围类外定义嵌套类时要在嵌套类的前面使用作用域解析运算符以指出这个嵌套类来是外围类A,如果不指定
就是重新定义一个新的同名类B。
11.4、在嵌套类外定义嵌套类中的成员:不能在外围类中定义嵌套类的成员,因为外围类和嵌套类是互相独立的。要在
嵌套类外定义嵌套类的成员必须在外围类外定义。且必须使用作用域解析运算符以指定这个成员是哪个外围类的,
比如嵌套类B中有成员void g()则在外围类A外定义的方法为:void A::B::g(){}第一个作用域解析运算符指出嵌套
类B的外围类是A,第二个作用域解析运算符指出函数g()是嵌套类B的成员函数。
11.5、在嵌套类中可以直接访问外围类中的静态成员,类型名和枚举成员。即在嵌套类中访问这些外围类的这些成员时
可以不使用作用域解析运算符,当然也可以使用。而在外围类的外面访问这些成员时必须使用作用域解析运算符。
11.6、外围类的对象同样不能直接访问嵌套类中的成员,但可以用作用域解析运算符来访问,这条规则只适合于公有成
员变量,对成员函数则是错误的,原因在下面说明。比如有外围类A和外围类A的成员变量a,嵌套类B,和嵌
套类B中的公有成员变量b,在main函数中有语句A m;m.b=3则是错误的,而m.B::b=3;就是正确的访问方式。
但反过来用嵌套类B 的对象访问外围类B 中的成员就不行了,因为嵌套类不知道外围类中的成员,比如main 函
数中有语句B n; n.A::a=2;就是错误的,因为嵌套类B不知道外围类A的存在,所以是错误的。
11.7、当使用上面的方法使用外围类访问嵌套类的非静态成员函数时,因为外围类和嵌套类的非静态成员函数都有隐藏的
this指针,而外围类的this指针指向外围类的对象,嵌套类的this指针指向嵌套类的对象,这时就存在this指针指
向不同的对象的问题。比如A m; m.B::g();这时就会出现外围类的this指针无法转换为嵌套类的this指针的错误,
所以不能使用上面的方法访问嵌套类中的成员函数。
11.8、访问控制权限同样适用于嵌套类,当嵌套类为公有,私有和保护的访问权限时,嵌套类同样遵守成员变量的私有,
公有和保护的特性。
11.9 嵌套类的构造函数和析构函数:在创建一个外围类的对象时先执行嵌套类的构造函数然后再执行外围类的构造函
数,析构函数则以相反的方式执行。对于嵌套类只要知道构造函数和析构函数的执行顺序就行了,其余的问题不
做介绍。
11.10 创建嵌套类的对象的方法,比如类B是类A的嵌套类,则创建类B的对象的方法为A::B mab;这时只会调用嵌套类
中的构造函数,而不会调用外围类A的构造函数,因为这里只创建了嵌套类B的对象。
嵌套类举例:
class mn+class onpublic:int dd;};//声明一个私有的嵌套类r
public: int a,b; static int e; const static int ee=0;
//定义一个嵌套类B开始
class snpublic:int c,d;  
//在嵌套类中访问外围类的成员的方法
void gb(){c=9;
//a=1;//错误,在嵌套类|中不能访问外围类~中的成员。因为嵌套类的成员和外围类的成员是互相独立的。
//…††‡ˆ‰*ŠŠ错误,也不能以这种方式来访问外围类中的成员a,因为a是非静态成员。
‘+’*+//要访问外围类中的成员,必须以外围类的指针或者对象的方式访问。
m.a=1;cout<<"gb() —††’e‡ˆ˜<<m.a<<endl;
m.ga();} 
void gb1(); };//在嵌套类š中声明一个在外围类œ定义的函数。
//定义嵌套类B结束
// void ††ž‰&)n$Ÿ ¡%%˜˜%%¢£¤S*f+ŠŠ错误,不能在外围类¦中定义嵌套类¨的成员。只能在外围类ª的外面定义。因为嵌套类的成员和外围
类的成员是互相独立的。
°+£*//可以在外围类中声明嵌套类的对象。
//在外围类中访问嵌套类的成员的方法
void ga(){a=9;
//c=µ*+ŠŠ错误,外围类·不能访问嵌套类¹中的成员。因为嵌套类的成员和外围类的成员是互相独立的。
//»††$ˆµ*+ŠŠ错误,不能以这种方式来访问嵌套类中的成员,因为成员c不是静态成员。
n.c=½*+$Ÿ ¡%%"gc() ¾††£e$ˆ˜<<n.c<<endl; //正确,要在外围类中访问嵌套类中的成员,必须以嵌套类的指针或对象的方式访问。
Ä+’¤*+’¤e¤¤ˆÅÅ*+$Ÿ ¡%%"md.dd="<<md.dd<<endl; } //正确,私有嵌套的类可以在外围类中使用。
class Ë*+f*+//在类Ì中声明嵌套类Î,以便在类Ï的外面定义嵌套类Ñ。
//在外围类外定义嵌套类的方法及访问外围类的静态成员
int Ò††¢ˆÓ*+//被始化外围类Õ中的静态成员e
class Ö††Ë+//在外围类的外部定义嵌套类的方法,在外围类外定义嵌套类应使用作用域解析运算符以指定嵌套类ß来自外围类á。
{public: void gc(){e=â*$Ÿ ¡%%m††¢%%¢¢%%m††¢¢%%¢£¤S*ff*+//可以在嵌套类中直接该问外围类中的静态成员,静态常量成员,类型名和
枚举成员,也可以在嵌套类中加上作用域解析运算符。在外围类外访问这些成员就必须要使用作用域解析符。
void ð††s††žñ1(){cout<<"gb1()"<<endl;} //在外围类的外部定义嵌套类中的成员必须要使用作用域解析符以指定嵌套类的外围类是ó。
//main函数开始
int main()
{ô+’‡*++//声明一个外围类的对象。构造函数的执行顺序是先构造嵌套类然后才是外围类,析构函数则按相反的顺序执行。构造函数的例子
就不举例了,只要知道构造的先后顺序就行了
//ma.c=9:;<=>?@A:BCC错误,成员c和gb()不是类E的成员,而是嵌套类H的成员。
ma.n.c=I9:BJKLMNN"class OB;<.n.c="<<ma.n.c<<endl; //通过在外围类中声明的嵌套类T的对象n来访问嵌套类V中的成员。
ma.n.gb();
ma.WXXJY999:BJKLMNN"ma.ZXXJY[<<ma.\XXJNN]^_`:B//正确,可以用作用域解析符以指定成员c是来自嵌套类的。
//ma.hXX>?@A:BCC错误,嵌套类j和外围类l的非静态成员函数的this指针指向不同的对象,在这里就不能把指向外围类|对象的this指针转
换为指向嵌套类对象的this指针,所以出现错误。
†XXHB;?:B//外围类外声明一个嵌套类的对象的方法。在这里必须要在嵌套类的前面用作用域解析操作符指出嵌套类š来自外围类œ。这里只
执行嵌套类的构造函数,不执行外围类的构造函数。因为只创建了嵌套类的对象。
mb.gb1();
//mb.gc(); //错误,gc()是外围类¤的成员,不是嵌套类¦的成员。
//mb.§XX<Y¨¨:BBCC错误,外围类的对象可以这样访问嵌套类中的成员变量,但反过来就不行了。因为µ是嵌套在类·中的,所以在这里嵌套
类¹并不知道有类¼的存在,在这里就会认类外围类À不是嵌套类Â的成员的错误。
//ÃXXÄBmd; //错误,嵌套类Æ是私有的,不能在外围类的外面声明私有的嵌套类的对象。
}
12、友元
1、声明友元的目的是让类的非成员函数或者类能访问该类对象的私有和受保护成员,一但把函数或者类声明为友元那么
就可以访问该类对象的私有和受保护的成员,注意是能访问类对象的私有和受保护成员不是类的私有和受保护成员。
比如class A{int a; pulbic:friend void g();} 则void g(){a=2;}就是错误的因为友元函数不能直接访问类中的私有成员只能
通过类的对象来访问私有成员,即void g(){A m; m.a=2;}就是正确的,通过类A的对象m来访问类中的私有成员。对
于类的友元类也是同样的情况。
2、friend出现的位置对友元来说无关紧要。即把友元声明在公有私有和受保护的位置都是一样的。
3、怎样在类中定义友元函数或友元类:要在类中定义友元函数或者友元类那么该类或者函数必须在外围类外进行声明,
注意是声明不是定义,声明的位置无关紧要,只要在使用之前声明一下就行,如果在全局范围类声明了友元函数则函
数就可以在全局范围类使用,如果在局部声明则只能在局部使用。如果既在类中定义友元函数或友元类,又在类外定
义,则会出现二重定义的错误。如果只在类中定义友元函数或友元类而在类外不进行声明,则将出现无法访问到该友
元的情况,当然该程序能通过编译,只是无法访问友元而已。比如有类class A{friend void g(){cout<<”g”<<endl;}如果
不在类A外再声明一下友元函数g,则程序中将无法调用友元函数g,既A m; m.g(); A::g()或者g();这些调用都是错误
的,前两个调用是因为g不是类A的成员,后一个调用会出现无法找到标识符g的错误。如果再在类外定义g函数,
比如在类外这样定义void g(){cout<<”g1”<<endl;}则会出现重复的g 函数的定义的错误。要正确使用在类中定义的友
元函数或友元类就必须在类A的外面重新声明一下函数g,比如void g();声明而不定义,这样的话程序就只须直接调
用该函数了,即语法g();将是正确的调用方法。
//友元示例
class ÉÊint a;
friend class Ë:BB//friend出现的位置对友元来说无关紧要。
public: friend void g();
friend void f(){cout<<”f”<<endl;}}; //在类中定义友元函数
void g(){//a=Ö:CC错误,友元不能直接访问类中的私有成员,只能访问该类的对象的私有成员。
ÛB;:B;=<YÖ:JKLMNN;=<NN]^_`: //正确,要使用友元访问类中的私有成员应通过对象来访问。
void f();  f();}  //正确,要访问在类中定义的友元函数f,则只须在使用之前先进行声明即可,声明的位置无关紧要。
class èÊpublic:void gb(){//a=é:CC错误,友元不能直接访问类中的私有成员,只能访问该类的对象的私有成员。
ìB;:B;=<Y9:JKLMNN;=<NN]^_`:íí://正确,要使用友元访问类中的私有成员应通过对象来访问。
int main()  {g();  ðB^:B^=>?@A:BBvoid f(); f();}//访问类中定义的友元的示例,使用之前要进行声明,如果在全局范围类声明了友元
函数f()则函数f()就可以在全局范围类使用。如果在局部声明则只能在局部使用。
作者:黄邦勇帅
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值