7章 类与对象
7.1 类和对象(定义及使用)初步
7.2成员函数的重载
7.3 对象的初始化、构造函数与析构函数
7. 4 类的定义及其使用
7. 5 类的静态成员及常量成员
7. 6 友元
7. 7 类之间的对象关系 对象成员和嵌套
7. 8 自定义类中的运算符重载
*7. 9 结构与联合
已在“4.6 结构体类型”一节中简单地介绍过结构及其使用。当时只是将它作为一种存放数据的实体来使用来看待的。实际上,C++中的结构体同样能够像类那样地实现封装(而在C中则不能)。即是说,除数据成员外,也可在结构中定义函数成员;结构也可以有它自己的构造函数、析构函数以及this指针等等。可以看出,类和结构在一定意义上是等价的。
结构与类的区别是:在缺省情况下,结构的成员是公有的(隐含为public型的),而类的成员是私有的(隐含为private型的)。作为使用习惯,通常在仅描述数据成员时使用结构struct,而当既描述数据成员又刻画其操作(函数成员)时使用类class。另外,在讨论结构的时候,我们往往用结构变量这个词;但在讨论类时,我们用对象这个词。
联合在C++语言中除了类似于类和结构之外,受到了更多的限制,主要是其所有数据成员重叠,都从同一位置开始存储。通常也只在要描述一批可重叠存放的数据成员时才使用联合union。
总之,C++语言中的类和对象概念使得结构和联合在程序设计中的重要性变轻,在作为C++语言的衍生语言Java语言中,干脆取消了结构和联合这两种数据类型。如果想对结构和联合了解的更多,请参阅有关随机帮助资料或
其他参考书籍。
7. 10 拷贝构造函数
构造函数除了可以在创建对象时对其中的成员数据进行初始化之外,还具有其他的两类特殊用途,即 利用构造函数进行类型转换,以及具有复制功能的构造函数。
7. 10. 1 复制构造函数
具有 完成复制功能的构造函数 称为复制构造函数。复制构造函数是一种特殊的构造函数。
其格式如下:
<类名> ::<类名>( 〈〈const〉〉<类名>&<参数表> )
{---}
复制构造函数的参数为它所在类的类别的引用。它是一个构造函数,当用一个已有的对象对正在创建的对象进行初始化时,调用该构造函数。它的特殊功能是能将参数代表的对象
逐域复制到新创建的对象中。
复制构造函数有两种形式:
1)系统自动产生。
当用户没有定义复制构造函数时,系统将会自动产生一个复制构造函数。
在下述3种情况下,系统都将自动地去调用对象所属类的拷贝构造函数:
①当使用如下两种方式之一的说明语句,用已存在的对象来创造一个相同的新对象时;
<类名><对象名2 >(<对象名1 >);
<类名><对象名2 >=<对象名1 >;
在说明新对象<对象名2 >时,准备用已存在对象<对象名1 >来对其进行初始化。
②若对象作为函数的赋值参数,在调用函数时,当刚进入被调函数处首先要进行实参和形参的结合,此时会自动调用拷贝构造函数,以完成由实参对象来创建一个相同的(局部于本函数的)形参新对象。
③若函数的返回值是类的对象,在执行被调函数的返回语句后(也即在函数调用完成返回时),系统也将自动调用拷贝构造函数去创建一个与返回值相同的临时新对象 。
一般规定所创建的临时对象,仅在创建它们的外部表达式范围内有效,表达式结束时,系统将调用析构函数去“销毁”该临时对象。
例1 调用自动复制构造函数
# include <iostream.h>
class CPoint
{ int x , y ;
public :
CPoint ( ) {x=0 , y=0 ;}
CPoint ( int vx , int vy )
{ x =vx ; y=vy ; }
void Print ( ) {cout<<x<<’ \ t ‘<<y<<’ \ n ‘ ; }
} ;
void main ( )
{ CPoint pt1 (100,200) ;
CPoint pt2(pt1) ; // A调用复制构造函数
CPoint pt3=pt1 ; //B调用复制构造函数
pt3=pt2 //C 同类型变量之间的赋值,不需要调用复制构造函数
pt1.Print( ) ;
pt2.Print( ) ;
pt3.Print( ) ;
}
执行该程序,输出结果为:
100 200
100 200
100 200
对于A行和B行,由于在类定义中没有相应的构造函数与它们匹配,此时将调用系统自动产生的复制构造函数。其具有如下的形式:
CPoint::CPoint(CPoint &pt)
{ x = pt.x ; y = pt.y ;}
而对于C行,因为是同类型变量之间的赋值,因此不需要调用任何构造函数,当然也不需要调用复制构造函数。
2) 用户自定义。
可以在类中定义一个复制构造函数,此时系统将不再自动产生复制构造函数。
例2 利用自定义的复制构造函数
# include <iostream.h>
class CPoint
{ int x , y ;
public :
CPoint (){x=0 ,y=0 ;}
CPoint( int vx , int vy )
{ x =vx ; y=vy ;}
CPoint(CPoint &pt)
{ x = pt.x ; y= pt.y ;
cout <<”调用复制构造函数CPoint(CPoint&)! \ n “ ;
}
void Print ( ) {cout<<x<<’\t’<<y<<’\n” ; }
};
void main ( )
{ CPoint pt1 (100 ,200) ;
pt1.Print ( ) ;
CPoint pt2 (pt1) ; //A 调用复制构造函数
pt2.Print ( ) ;
CPoint pt3 =pt1 ; //B调用复制构造函数
pt3.Print ( ) ;
cout<<”标志---\ n “ ; //C
pt3=pt2 ; //C
}
运行结果:
100 200
调用复制构造函数CPoint(CPoint&)!
100 200
调用复制构造函数CPoint(CPoint&)!
100 200
标志---
3)一般来说,系统自动产生的复制构造函数能处理大多数问题,但有些问题却必须利用用户自定义的复制构造函数。比如在复制同类型对象时只复制部分成员数据,或者类中的成员数据用new运算符动态分配存储空间且在析构函数中释放存储空间等。
例:3自定义的复制构造函数处理“指针悬挂”问题。
# include<iostream.h>
# include<string.h>
class CA
{ char *ps ;
public :
CA( ) {ps=0 ;}
CA (char *s)
{ ps=new char [ strlen (s)+1 ] : //为指针ps动态分配存储空间
strcpy (ps , s) ;
}
CA ( CA &s) ;
~CA(){if (ps ) delete [ ] ps ; } //撤消指针ps所占用的存储空间
char *GetS ( ) { return ps ;}
} ;
CA ::CA(CA &s)
{ if (s.ps )
{ // 如果s中的成员数据非空的话
ps = new char [strlen (s.ps)+1] ; // 为当前对象的成员数据动态分配存储空间
strcpy (ps , s.ps ) ; //将s中的成员数据复制给当前对象成员数据
}
else ps=0 ;
}
void main (void )
{ CA s1(“China!”) ;
CA s2(s1) ; //A调用复制构造函数
cout<<”s1=”<<s1.GetS()<< ’\t ‘ ;
cout<<”s2=”<<s2.GetS()<< ’\n ‘ ;
}
执行该程序,输出结果为:
s1=China