第十讲对象的赋值和复制.docx
- 文档编号:9292895
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:19
- 大小:24.78KB
第十讲对象的赋值和复制.docx
《第十讲对象的赋值和复制.docx》由会员分享,可在线阅读,更多相关《第十讲对象的赋值和复制.docx(19页珍藏版)》请在冰豆网上搜索。
第十讲对象的赋值和复制
第十讲:
对象的赋值和复制
本讲基本要求
*掌握:
类模板的声明及引用;对象的赋值和复制。
*理解:
静态成员和静态成员函数的定义及引用;友元、友元类的定义;友元函数的声明及引用。
重点、难点
*类模板的声明及引用;对象的赋值和复制。
一、对象的赋值和复制
1、对象的赋值
如果对一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值,或者说,一个对象的值可以赋给另一个同类的对象。
这里所指的对象的值是指对象中所有数据成员的值。
对象之间的赋值也是通过赋值运算符“=”进行的。
本来,赋值运算符“=”只能用来对单个的变量赋值,现在被扩展为两个同类对象之间的赋值,这是通过对赋值运算符的重载实现的(关于运算符的重载将在第4章中介绍)。
实际上这个过程是通过成员复制(memberwisecopy)来完成的,即将一个对象的成员值一一复制给另一对象的对应成员。
对象赋值的一般形式为:
对象名1=对象名2;
注意:
对象名l和对象名2必须属于同一个类。
例如
Studentstudl,stud2;//定义两个同类的对象
stud2=studl; //将studl各数据成员的值赋给stud2
通过下面的例子可以了解怎样进行对象的赋值。
例9对象的赋值。
#include
usingnamespacestd;
classBox
{ public:
Box(int=10,int=10,int=10);//声明有默认参数的构造函数
intvolume();
private:
intheight;
intwidth;
intlength;};
Box:
:
Box(inth,intw,intlen)
{ height=h;
width=w;
length=len;}
intBox:
:
volume()
{ return(height*width*length);}//返回体积的值
intmain()
{ Boxbox1(15,30,25),box2;//定义两个对象box1和box2
cout<<"Thevolumeofbox1is"< box2=box1;//将box的值财给1box2 cout<<"Thevolumeofbox2is"< return0;} 运行结果如下: ThevolumeOfboxlis11250 ThevolumeOfbox2is11250 说明: (1)对象的赋值只对其中的数据成员赋值,不对成员函数赋值。 数据成员是占存储空间的,不同对象的数据成员占有不同的存储空间,赋值的过程是将一个对象的数据成员在存储空间的状态复制给另一对象的数据成员的存储空间。 而不同对象的成员函数是同一个函数代码段,不需要、也无法对它们赋值。 (2)类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果(在此不作详细分析,只需记住这一结论即可)。 2、对象的复制 有时需要用到多个完全相同的对象: 其一般形式为: 类名对象2(对象1);//用对象l复制出对象2。 有这就是对象的复制机制。 用一个已有的对象快速地复制出多个完全相同的对象。 如: Boxbox2(boxl);//其作用是用已有的对象boxl去克隆出一个新对象box2。 可以看到: 它与前面介绍过的定义对象方式类似,但是括号中给出的参数不是一般的变量,而是对象。 在建立一个新对象时调用一个特殊的构造函数——复制构造函数(copyconstructor)。 这个函数的形式是这样的: //Thecopyconstructordefinition Box: : Box(constBox&b) { height=b.height; width=b.width; length=b.length;} 说明: 1、复制构造函数也是构造函数,但它只有一个参数,这个参数是本类的对象(不能是其他类的对象),而且采用对象的引用的形式(一般约定加const声明,使参数值不能改变,以免在调用此函数时因不慎而使对象值被修改)。 此复制构造函数的作用就是将实参对象的各数据成员值一一赋给新的对象中对应的数据成员。 2、复制对象的语句Boxbox2(boxl);这实际上也是建立对象的语句,建立一个新对象box2。 由于在括号内给定的实参是对象,因此编译系统就调用复制构造函数(它的形参也是对象),而不会去调用其他构造函数。 实参boxl的地址传递给形参b(b是boxl的引用),因此执行复制构造函数的函数体时,将boxl对象中各数据成员的值赋给borg中各数据成员。 3、用户可以在声明类时定义复制构造函数,如果用户自己未定义复制构造函数,则编译系统会自动提供一个默认的复制构造函数,其作用只是简单地复制类中每个数据成员。 4、还提供另一种方便用户的复制形式,用赋值号代替括号. 其一般形式为: 类名对象名1=对象名2; 如: Boxbox2=box1;//用boxl初始化box2 可以在一个语句中进行多个对象的复制,但是其作用都是调用复制构造函数。 5、对象的复制和1节介绍的对象的赋值在概念上和语法上的不同。 请注意普通构造函数和复制构造函数的区别。 (1)在形式上 类名(形参表列); //普通构造函数的声明,如Box(int,intw,intleu); 类名(类名&对象名);//复制构造函数的声明,如Box(Box&b); (2)在建立对象时,实参类型不同。 系统会根据实参的类型决定调用普通构造函数或复制构造函数。 如 Boxboxl(12,15,l6);//实参为整数(普通数据类型),调用普通构造函数 Boxbox2(boxl); //实参是对象名(类数据类型),调用复制构造函数 (3)在什么情况下被调用 普通构造函数在程序中建立对象时被调用。 复制构造函数在用已有对象复制一个新对象时被调用,在以下3种情况下需要克隆对象: ①程序中需要新建立一个对象,并用另一个同类的对象对它初始化。 ②函数的参数为类的对象时,在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个实参的拷贝,这就是按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。 ③函数的返回值是类的对象。 在函数调用完毕需要将返回值(对象)带回函数调用处时。 此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。 二、静态成员 学习C语言时已了解全局变量,它能够实现数据共享。 如果在一个程序文件中有多个函数,在每一个函数中都可以改变全局变量的值,全局变量的值为各函数共享。 但是用全局变量时安全性得不到保证,由于在各处都可以自由地修改全局变量的值,很有可能偶一失误,全局变量的值就被修改,导致程序的失败。 因此在实际工作中很少使用全局变量。 如果想在同类的多个对象之间实现数据共享,也不要用全局对象,可以用静态的数据成员。 1、静态数据成员 静态数据成员是一种特殊的数据成员。 它以关键字static开头。 静态数据成员定义一般形式: static数据类形静态数据成员 静态数据成员赋值一般形式: 数据类型类名: : 静态数据成员名=初值;//只能在类体外进行初始化。 静态数据成员引用一般形式: 类名: : 静态数据成员名//通过类名引用静态数据成员 对象.静态数据成员名 //通过对象名引用静态数据成员 例: classBox { public: intvolume(); private: staticintheight;//把height定义为静态的数据成员 intwidth; intlength;}; 例10引用静态数据成员。 #include usingnamespacestd; classBox { public: Box(int,int); intvolume(); staticintheight;//把height定义为公用的静态的数据成员 intwidth; intlength;}; Box: : Box(intw,intlen)//通过构造函数对width和length赋初值 { width=w; length=len;} intBox: : volume() { return(height*width*length);} intBox: : height=10;//对静态数据成员height初始化 intmain() { Boxa(15,20),b(20,30); cout< cout< cout< : height< cout< return0;} 说明: (1)如果只声明了类而未定义对象,则类的一般数据成员是不占内存空间的,只有在定义对象时,才为对象的数据成员分配空间。 静态数据成员不属于某一个对象,在为对象所分配的空间中不包括静态数据成员所占的空间。 静态数据成员是在所有对象之外单独开辟空间。 只要在类中定义了静态数据成员,即使不定义对象,也为静态数据成员分配空间,它可以被引用。 在一个类中可以有一个或多个静态数据成员,所有的对象共享这些静态数据成员,都可以引用它。 (2)静态数据成员不随对象的建立而分配空间,也不随对象的撤销而释放(一般数据成员是在对象建立时分配空间,在对象撤销时释放)。 静态数据成员是在程序编译时被分配空间的,到程序结束时才释放空间。 (3)静态数据成员可以初始化,但只能在类体外进行初始化。 注意: 不能用参数初始化表对静态数据成员初始化。 如在定义Box类中这样定义构造函数是错误的: Box(inth,intw,intlen): height(h){}//错误,height是静态数据成员如果未对静态数据成员赋初值,则编译系统会自动赋予初值0。 (4)静态数据成员既可以通过对象名引用,也可以通过类名来引用。 注意: 在上面的程序中将height定义为公用的静态数据成员,所以在类外可以直接引用,可以看到在类外可以通过对象名引用公用的静态数据成员,也可以通过类名引用静态数据成员 如上例中: a.height//通过对象名a引用静态数据成员。 Box: : height//通过 如果静态数据成员被定义为私有的,则不能在类外直接引用,而必须通过公用的成员函数引用。 (5)有了静态数据成员,各对象之间的数据有了沟通的渠道,实现数据共享,因此可以不使用全局变量。 全局变量破坏了封装的原则,不符合面向对象程序的要求。 (6)公用静态数据成员与全局变量的不同,静态数据成员的作用域只限于定义该类的作用域内(如果在一个函数中定义类,那么其中静态数据成员的作用域就是此函数内)。 在此作用域内,可以通过类名和域运算符“: : ”引用静态数据成员,而不论类对象是否存在。 2、静态成员函数 静态成员函数定义一般形式: static数据类型静态成员函数 静态数据成员函数引用一般形式: 类名: : 静态函数成员名//通过类名引用静态数据成员 对象.静态函数成员名 //通过对象名引用静态数据成员 作用: 与静态数据成员不同,静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员。 静态成员函数与非静态成员函数的根本区别是: 非静态成员函数有this指针,而静态成员函数没有this指针。 由此决定了静态成员函数不能访问本类中的非静态成员数据。 说明: 静态成员函数可以直接引用本类中的静态数据成员,因为静态数据成员同样是属于类的,可以直接引用。 在程序中,静态成员函数主要用来访问静态数据成员,而不访问非静态成员。 并不是绝对不能引用本类中的非静态成员,只是不能进行默认访问,因为无法知道应该去找哪个对象。 如果一定要引用本类的非静态成员,应该加对象名和成员运算符“.”。 例11静态成员函数的应用。 #include usingnamespacestd; classStudent //定义Student类 {public: Student(int,int,int);//定义构造函数 voidtotal(); staticfloataverage();//声明静态成员函数 private: intnum; intage; floatscore; staticfloatsum; //静态数据成员 staticintcount;};//静态数据成员 Student: : Student(intm,inta,ints) {num=m; age=a; score=s;} voidStudent: : total()//定义非静态成员函数 {sum+=score; //累加总分 count++;} //计已统计的人数 StaticfloatStudent: : average()//定义静态成员函数 {return(sum/count);} //使用静态数据成员 floatStudent: : sum=0;//对公用静态数据成员初始化 intStudent: : count=0;//对公用静态数据成员初始化 intmain() { Studentstud[3]={//定义对象数组并初始化 Student(1001,18,70), Student(1002,19,79), Student(1005,20,98)}; intn; cout<<"pleaseinputthenumberofstudents: "; cin>>n; //输入需要求前面多少名学生的平均成绩 for(inti=0;i stud[i].total(); cout<<"Theaveragescoreof"< Student: : average() return0;} 运行结果为 pleaseinputthenumberOfstudents: 3/ theaveragescoreof3studentsis82.3333 说明: (1)在主函数中定义了stud对象数组,为了使程序简练,只定义它含3个元素,分别存放3个学生的数据(每个学生的数据包括学号、年龄和成绩)。 程序的作用是先求用户指定的n名学生的总分,然后求平均成绩(n由用户输入)。 (2)在Student类中定义了两个静态数据成员sum(总分)和count(累计需要统汁的学生人数),这是由于这两个数据成员的值是需要进行累加的,它们并不是只属于某一个对象元素,而是由各对象元素共享的,可以看出: 它们的值是在不断变化的,而且无论对哪个对象元素而言,都是相同的,而且始终不释放内存空间。 (3)total是公有的成员函数,其作用是将一个学生的成绩累加到snm中。 公有的成员函数可以引用本对象中的一般数据成员(非静态数据成员),也可以引用类中的静态数据成员。 score是非静态数据成员,sum和count是静态数据成员。 (4)average是静态成员函数,它可以直接引用私有的静态数据成员(不必加类名或对象名),函数返回成绩的平均值 (5)在main函数中,引用total函数要加对象名(今用对象数组元素名),引用静态成员函数average函数要用类名或对象名。 (6)请思考: 如果不将average函数定义为静态成员函数行不行? 程序能否通过编译? 需要作什么修改? 为什么要用静态成员函数? 请分析其理由。 (7)如果想在average函数中引用stud[l]的非静态数据成员score,应该怎样处理? 有人在上面的程序基础上将静态成员函数average改写为 floatStudent: : average()//定义静态成员函数 { cout< return(sum/count);} 结果发现在编译时出错。 可以将average函数的定义改为 floatStudent: : average(Studentstu)//函数参数为对象 { cout< return(sum/count); 以上是在例11基础上顺便说明静态成员函数引用非静态数据成员的方法,以帮助理解。 但是在程序中最好养成这样的习惯: 只用静态成员函数引用静态数据成员,而不引用非静态数据成员。 这样思路清晰,逻辑清楚,不易出错。 三、友元 友元定义: 友元可以访问与其有好友关系的类中的私有成员。 友元包括友元函数和友元类。 定义格式: friend友元函数和友元类 友元说明: 在一个类中可以有公用的(public)成员和私有的(pnvate)成员,我们曾用客厅比喻公用部分,用卧室比喻私有部分。 在类外可以访问公用成员,只有本类中的函数可以访问本类的私有成员。 现在,我们来补充介绍——个例外——友元(friend)。 1、友元函数 如果在本类以外的其他地方定义了一个函数(这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数),在对本类进行声明时在类体中用friend对该函数进行声明,此函数就称为本类的友元函数。 一个类的友元函数可以访问这个类中的私有成员。 ①将普通函数声明为友元函数 通过下面的例子可以了解友元函数的性质和作用。 例12友元函数的简单例子。 #include usingnamespacestd; classTime { public: Time(int,int,int); friendvoiddisplay(Time&);//声明display函数为Time类的友元函数 private: //以下数据是私有数据成员 inthour; intminute; intsec;}; Time: : Time(inth,intm,ints)//定义构造函数,给hour,minute,sec赋初值 { hour=h; minute=m; sec=s;} voiddisplay(Time&t)//这是友元函数,形参t是Time类对象的引用 {cout< "< "< intmain() {Timet1(10,13,56); display(t1);//调用display函数,实参t1是Time类对象 return0;} 程序输出结果如下: 10: 23: 56 注意: 1、display是一个在类外定义的且未用类Time作限定的函数,它是非成员函数,不属于任何类。 它的作用是输出时间(时、分、秒)。 如果在Time类的定义体中未声明display函数为friend函数,它是不能引用Time中的私有成员hour,minute,sec。 2、由于声明了display是Time类的friend函数,所以display函数可以引用Time中的私有成员hour,minute,sec。 但注意在引用这些私有数据成虽时,必须加上对象名。 因为display函数不是Time类的成员函数,不能默认引用Time类的数据成员,必须指定要访问的对象。 ②友元成员函数 friend函数不仅可以是一般函数(非成员函数),而且可以是另一个类中的成员函数。 例13友元成员函数的简单应用。 在本例中除了介绍有关友元成员函数的简单应用外,还将用到类的提前引用声明,请读者注意。 请阅读下面的程序: #include usingnamespacestd; classDate;//对Date类的提前引用声明 classTime//定义Time类 { public: Time(int,int,int); voiddisplay(constDate&);//display是成员函数,形参是Date类对象的引用 private: inthour; intminute; intsec;}; classDate//声明Date类 { public: Date(int,int,int); friendvoidTime: : display(constDate&);//声明Time类中的display函数为本类的友元成员函数 private: intmonth; intday; intyear;}; Time: : Time(inth,intm,ints)//定义类Time的构造函数 {hour=h; minute=m; sec=s;} voidTime: : display(constDate&da)//display函数的作用是输出年、月、日和时、分、秒 {cout< cout< "<
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第十 对象 赋值 复制