版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第3章类和对象3.1
类3.2
对象3.3
构造函数和析构函数3.4类的聚集——对象成员(自学)3.5
静态成员3.7
综合举例3.1类类描述了一组具有相同属性和行为特征的对象。在系统实现中,类是一种共享机制,它提供了本类对象共享的操作实现。类是代码复用的基本单位,它可以实现抽象数据类型,创建对象,实现属性和行为的封装。类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。数据结构操作1操作2操作3操作4操作5数据结构3.1类对象是类的实例。类是对一组具有相同特征的对象的抽象描述,所有这些对象都是这个类的实例。在程序设计语言中,类是一种数据类型,而对象是该类型的变量,变量名即是某个具体对象的标识。类提供了完整的解决特定问题的能力,因为类描述了数据结构(对象属性)、算法(对象行为)和外部接口(消息协议)。在C++语言中,一个类的定义包含数据成员和成员函数两部分内容。数据成员定义该类对象的属性,不同对象的属性值可以不同;成员函数定义了该类对象的操作即行为。类具有对数据的抽象性、隐藏性和封装性。类对象的行为由类的内部数据结构和相关的操作确定;外部行为通过操作接口实现。人们关心的就是操作接口所能提供的服务。
例…
3.1类ThepersonwatchingTVdoesnotneedtoknowtheTVset’sstructureandhowitworksinside.
ButheneedstocontroltheTVset.TVneedstoprovideaninterface.InternalState对象及其信息封装的例子:TV开请求变更频道调节声音TurnOnTurnOffChangeChannelAdjustVolumeOthers...被封装的行为和状态关请求class类名{private:
//私有数据成员和成员函数public:
//公有数据成员和成员函数protected:
//保护的数据成员和成员函数
};3.1.1类的定义类由三部分组成:类名、数据成员和成员函数。有关类定义的几点说明:(1)class是关键字,类名是一个标识符,必须符合C++标识符的命名规则。{}内是类的定义体部分,说明该类的成员,类的成员包括数据成员和成员函数。(2)类成员的三种访问控制权限:类有三种访问控制权限,分别是private(私有成员)、public(公有成员)、protected(保护成员),在每一种访问控制权限下,均可以定义数据成员和成员函数。
private:私有成员是在类中被隐藏的部分,它往往是用来描述该类对象属性的数据成员,私有成员只能由本类的成员函数或某些特殊说明的函数(如第4章讲到的友员函数)访问,而类的外部根本就无法访问。实现了访问权限的有效控制,有利于数据隐藏,使内部数据不能被任意的访问和修改,使数据得到有效保护,也不会对该类以外的其余部分造成影响,使模块之间的相互作用被降低到最小。有关类定义的几点说明:(1)class是关键字,类名是一个标识符,必须符合C++标识符的命名规则。{}内是类的定义体部分,说明该类的成员,类的成员包括数据成员和成员函数。(2)类成员的三种访问控制权限:类有三种访问控制权限,分别是private(私有成员)、public(公有成员)、protected(保护成员),在每一种访问控制权限下,均可以定义数据成员和成员函数。public:公有成员对外是完全开放的,公有成员一般是成员函数,它提供了外部程序与类的接口功能,用户通过公有成员访问该类对象中的数据。OOP的灵魂就是使用private隐藏类的实现,使用public暴露类的接口。
有关类定义的几点说明:(1)class是关键字,类名是一个标识符,必须符合C++标识符的命名规则。{}内是类的定义体部分,说明该类的成员,类的成员包括数据成员和成员函数。(2)类成员的三种访问控制权限:类有三种访问控制权限,分别是private(私有成员)、public(公有成员)、protected(保护成员),在每一种访问控制权限下,均可以定义数据成员和成员函数。protected:只能由该类的成员函数,友元,公有派生类成员函数访问的成员。保护成员与私有成员在一般情况下含义相同,它们的区别体现在类的继承中对产生的新类的影响不同,具体内容将在第5章中介绍。privatepublicdata1data2function1data3data4function2function3function4Membervisibilitystrangerfriend有关类定义的几点说明:(3)由于类的公有成员提供了一个类的接口,所以一般情况下,先定义公有成员,再定义保护成员和私有成员,这样可以在阅读时首先了解这个类的接口。类的设计是以数据为中心,还是以行为为中心?
classA{private:
inti,j;floatx,y;……public:
voidfunc1(void);
voidfunc2(void);……};classA{public:voidfunc1(void);voidfunc2(void);
……private:inti,j;floatx,y;……};有关类定义的几点说明:(4)结构体struct和类class的区别C语言中的结构体只有数据成员,无函数成员。C++语言中的结构体可有数据成员和函数成员。在缺省情况下,结构体中的数据成员和成员函数都是公有的,而在类中是私有的。可以随意修改结构体变量中的数据,这样对数据的操作是很不安全的,不能通过结构体对数据进行保护和控制;在结构体中,数据和其相应的操作是分离的,使得程序的复杂性难以控制,而且程序的可重用性不好,严重影响了软件的生产效率。
所以,一般我们仅在描述数据成员时使用结构体,当既有数据成员又有成员函数时使用类。classTdate{//定义日期类public://定义公有成员函数
voidSet(intm,intd,inty);//置日期值
intisLeapYear();//判是否闰年
voidPrint();//输出日期值private://定义私有数据成员
intmonth;
intday;
intyear;};//类定义体的结束有关类定义的几点说明:(5)类定义举例3.1.2类中成员函数的定义成员函数是程序算法实现的部分,是对封装的数据进行操作的唯一途径。类的成员函数有两种定义方法:外联定义和内联定义。1.外联函数外联函数是指在类定义体中声明成员函数,而在类外定义成员函数。
作用域区分符::指明一个成员函数或数据成员所在的类。
::前若不跟类名,则成为全局数据或全局函数(非成员函数)。class
Tdate{public:
voidSet(intm,intd,inty);
intisLeapYear();
voidPrint();
private:
intmonth;
intday;
intyear;};voidTdate::Set(intm,intd,inty){month=m;day=d;year=y;}intTdate::IsLeapYear(){return(year%4==0&&year%100!=0)||(year%400==0);}voidTdate::Print(){cout<<month<<"/"<<day<<"/"<<year<<endl;}外联函数在类定义体中声明,在类外定义2.
内联函数内联函数是指程序在编译时将函数的代码插入在函数的每个调用处,作为函数体的内部扩展,用来避免函数调用机制所带来的开销,提高程序的执行效率。可以将那些仅由少数几条简单代码组成,却在程序中被频繁调用的函数定义为内联函数。内联函数有两种定义方法,一种方法是在类定义体内定义成员函数,另一种方法是使用inline关键字。classTdate{public:
voidSet(intm,intd,inty)//内联函数
{month=m;day=d;year=y;}
intisLeapYear();//外联函数
voidPrint();//外联函数private:
intmonth;
intday;
intyear;};内联函数定义方法一:在类定义体内定义(隐式内联)classTdate{public:
voidSet(intm,intd,inty)//内联函数
{month=m;day=d;year=y;}
intisLeapYear();//外联函数
voidPrint();//外联函数private:
intmonth;
intday;
intyear;};内联函数定义方法二:使用关键字inline(显式内联)inline
intisLeapYear();inline
voidPrint();//内联函数//内联函数
通过在进行成员函数声明时使用关键字inline,可将原本在类声明之外定义的成员函数强制变成内联函数。
关键字inline必须在声明中使用,或在声明及定义中均使用。3.2对象在面向对象方法开发的软件系统中,对象是类的实例,是属性和服务的封装体。一个对象就是一个实际问题域中的一个实体,它包含了数据结构和所提供的相关操作功能,形成了一个“基本程序模块”。对象的属性用于描述对象的静态数据特征,如人类有大脑、五官、四肢;鸟有翅膀、羽毛;树有根、茎、叶等,这些都是描述对象的静态数据特征。对象的属性可用系统的或用户自定义的数据类型来表示,也可以用抽象的数据类型表示。对象属性值的集合称为对象的状态(state)。3.2.1对象的基本概念对象的服务用于描述对象的动态特征,它是定义在对象属性基础上的一组操作方法(method)的集合。如人类有思维能力、语言能力等;鸟有翅膀可以飞行等。对象的服务是响应消息而完成的算法,体现了对象的行为能力。对象的服务包括“自操作”和“它操作”,“自操作”是对象对其内部数据属性进行的操作,“它操作”是对其他对象进行的操作。当一个对象映射为软件实现时由三部分组成:(1)私有的数据结构:它用于描述对象的内部状态。(2)处理:也称为操作或方法,它施加于数据结构之上。(3)接口:这是对象可被共享的部分,消息通过接口调用相应的操作。它不提供操作是如何实现的信息。3.2.1对象的基本概念方法二:在使用时定义3.2.2对象的定义方法一:在定义类的同时直接定义class
Tdate{public:
voidSet(intm,intd,inty);
intisLeapYear();
voidPrint();
private:
intmonth;
intday;
intyear;}s1,s2;Tdates1,s2;……Tdates3;……与结构体变量的定义方式相同3.2.3对象成员的访问
需要注意:只有用public定义的公有成员才能使用圆点操作符访问。对象中的私有成员是类中隐藏的数据,类的外部不能访问对象的私有成员,只能通过该类的公有成员函数来访问它们。例…(1)通过对象调用成员格式:对象名.公有成员其中,“.”称为对象选择符,简称点运算符。(2)通过指向对象的指针调用成员格式:指向对象的指针->公有成员或(*对象指针名).公有成员(3)通过对象的引用调用成员格式:对象的引用.公有成员Tdates;Tdate*pTate=&s;s.Set(2,15,1998);pTdate->print();(*pTdate).print();
定义时钟类:classMyclock{
private:
inthour,minute,second;
public:
voidinit();
voidupdata();
voiddisplay();};Myclockclock,*pclock;
//定义对象clock和指向Myclock类对象的指针pclockclock.init();//通过对象访问公有成员函数pclock=&clock;//指针pclock指向对象clockpclock->display();//通过指针访问成员函数clock.hour=4;//错误,因为对象不能访问其私有成员clock.hour=4;错误,因为对象不能访问其私有成员(1)名字解析在调用成员函数时,通常使用缩写形式,如上例中的语句s.set(2,15,1998);就是s.Tdate::set(2,15,1998);的缩写,因此可以定义两个或多个类的具有相同名字的成员而不会产生二义性。(2)this指针当一个成员函数被调用时,系统自动向它传递一个隐含的参数,该参数是一个指向接受该函数调用的对象的指针,在程序中可以使用关键字this来引用该指针,因此称该指针为this指针。this指针是C++实现封装的一种机制,它将成员和用于操作这些成员的成员函数连接在一起。
3.2.4名字解析和this指针voidTdate::set(intm,intd,inty)//置日期值{month=m;day=d;year=y;}voidTdate::set(intm,intd,inty)//置日期值{
this->month=m;this->day=d;this->year=y;}使用this指针,保证了每个对象可以拥有不同的数据成员,但处理这些数据成员的代码可以被所有的对象共享。this指针同普通函数一样,类的成员函数也可以是带缺省值的函数,其调用规则同普通函数。成员函数也可以是重载函数,类的成员函数的重载与全局函数的重载方法相同。【例3.4】带缺省参数的成员函数。【例3.5】重载成员函数。3.2.5带缺省参数的成员函数和重载成员函数C++程序的多文件结构与模块化类一般情况下,模块化类的组成:规范说明部分:描述一个模块与其它模块的接口,常常作为头文件存放,扩展名为”.h”;实现部分:则是模块的实现细节,可能由多个扩展名为“.cpp”的文件组成。一般一个较大的文件可以分为三类文件来存放:(1)将类的说明作为一个头文件来存放。内联函数原型和定义归入头文件;(2)类的实现部分单独组成一个文件,用来存放类的成员函数的定义;(3)在main()中实现类的应用。通过将类模块化,并将其编译为目标格式,可以实现成员函数的定义对于类用户的隐藏。【例3.1+例3.2】
50综合举例1:
47综合举例2:创建一个数据结构:用圆心和半径抽象一个圆
//tdate.h
这个头文件只存放有关Tdate类的定义说明#ifndefTdate_H
//用来避免重复定义#defineTdate_H //不是类的一部分classTdate{public:
voidset(int,int,int); //成员函数原型
intisLeapYear(); //成员函数原型
voidprint(); //成员函数原型private:
intmonth;
intday;
intyear;}; //Tdate类定义的结束#endif
//Tdate_H1.定义头文件:包含类的说明,内联函数的原型和定义前两行的作用:如果一个程序系统中的多个文件均包含Tdate类,则在编译时可避免Tdate类中标识符的重复定义。如果已定义,可使编译器跳过#endif之前的所有行。
//tdate.cpp
类Tdate的实现部分#include<iostream>/*因为tdate.cpp文件要访问运算符<<和ostream类对象cout,而这二者都是定义在iostream类中的,所以包含iostream头文件*/
#include"tdate.h"//包含用户自定义的头文件,该文件中提供了Tdate类的定义usingnamespacestd;voidTdate::set(intm,intd,inty){ month=m;day=d;year=y;}intTdate::isLeapYear(){ return((year%4==0&&year%100!=0)||(year%400==0)); }
voidTdate::print(){ cout<<month<<"/"<<day<<"/"<<year<<endl; }2.类的实现:包含类中各成员函数的定义(可为每个类建一个cpp文件)
//ch3_2.cpp#include<iostream>#include"tdate.h"usingnamespacestd;voidsomeFunc(Tdate&refs){ refs.print(); //通过对象的引用调用成员函数
if(refs.isLeapYear())cout<<"error\n";//通过对象的引用调用成员函数
elsecout<<"right\n"; }intmain() //类的应用部分{Tdates,*pTdate=&s; //pTdate为指向Tdate类对象的指针s.set(2,15,1998); //通过对象调用成员函数pTdate->print(); //通过指向对象的指针调用成员函数if((*pTdate).isLeapYear())cout<<"error\n";//通过指向对象的指针调用成员函数
elsecout<<"right\n";someFunc(s); //对象的地址传给引用
return0;}3.类的应用:包含main()函数,在其中使用不同的类3.3构造函数和析构函数当建立一个对象时,对象的状态(数据成员的取值)是不确定的。但对象表达了现实世界的实体,因此,一旦建立对象,必须有一个有意义的初始值。也就是说,当一个对象出现时,必须立刻处于一种稳定的状态,并且在其生命期内保持稳定,这是OOP的原则之一。C++有一个称为构造函数的特殊成员函数,它可自动进行对象的初始化。而析构函数在对象撤消时执行清理任务,进行善后处理。构造函数和析构函数是类中的两个特殊的成员函数,具有普通成员函数的许多共同特性,但还具有一些独特的特性,我们可以把它们归纳成以下几点:构造函数和析构函数的特性(1)
它们都没有返回值说明,也就是说定义这两个函数时不能指出函数返回值的类型,即使是void也不能有。(2)
它们不能被继承。(第5章)(3)
构造函数可以有缺省参数。(4)
析构函数可以是虚的(virtual),但构造函数不可以是虚的。(第6章)(5)
不可取它们的地址。(6)
不能用常规调用方法调用构造函数;当使用完全的限定名(带对象名、类名和函数名)时可以调用析构函数。(7)
当定义对象时,编译程序自动调用构造函数;当删除对象时,编译程序自动地调用析构函数。对象的初始化是指对象成员数据的初始化,在使用对象前,一定要初始化。由于成员数据一般为私有的(private),所以不能直接赋值。c1.radius=5;//errorC2248:
'radius':
cannotaccessprivatememberdeclaredinclass'Circle'对象成员数据初始化有以下两种方法:1、在类中提供一个普通成员函数来初始化2、使用构造函数3.3.1构造函数例:voidsetCircle(int,int,int);
使用对象前必须显式调用该函数,例:c1.setCircle(3,4,1),但是这样会造成使用上的不便(使用对象前必须显示调用该函数)和不安全(未调用初始化函数就使用对象)。构造函数是一个与类同名,没有返回值的特殊成员函数。一般用于初始化类的数据成员,每当创建一个对象时(包括使用new动态创建对象),编译系统就自动调用构造函数。构造函数既可在类外定义,也可作为内联函数在类内定义。构造函数的声明格式为:(类外定义)
<类名>::构造函数名(<形式参数表>)【例3.6】构造函数的定义和调用。【例3.7】构造函数的重载【例3.8】带缺省参数的构造函数。1.构造函数(constructor)
60一个类可以有多个构造函数,因为这样可以有多种方法来初始化一个类的实例。对某些类来说,重载构造函数是很有意义的。构造函数是一个与类同名,没有返回值的特殊成员函数。一般用于初始化类的数据成员,每当创建一个对象时(包括使用new动态创建对象),编译系统就自动调用构造函数。构造函数既可在类外定义,也可作为内联函数在类内定义。构造函数的声明格式为:(类外定义)
<类名>::构造函数名(<形式参数表>)【例3.6】构造函数的定义和调用。【例3.7】构造函数的重载【例3.8】带缺省参数的构造函数。1.构造函数(constructor)构造函数也可以使用缺省参数,但要注意,必须保证函数形式参数缺省后,函数形式不能与其它构造函数完全相同。即在使用带缺省参数的构造函数时,要注意避免二义性。所带的参数个数或参数类型必须有所不同,否则系统调用时会出现二义性。
62C++规定,每个类必须有一个构造函数,没有构造函数,就不能创建任何对象。若用户未显式定义一个类的构造函数,则C++提供一个缺省的构造函数,也叫默认构造函数,该缺省构造函数是个无参构造函数,它仅负责创建对象,而不做任何初始化工作。只要一个类定义了一个构造函数,C++就不再提供默认的构造函数。如果为类定义了一个带参数的构造函数,还想要使用无参构造函数,则必须自己定义。2.默认构造函数【例3.9】默认构造函数
64默认构造函数的普遍意义:术语“默认构造函数”是指可以不使用参数来调用的任何构造函数,无论它是由编译器提供的,还是用户自己编写的。它可以有两形式:(1)其参数列表为空;(2)其参数列表全部由默认值组成。在一个类中可以有多个构造函数(重载),但是默认构造函数只能有一个,否则编译器将不知道究竟该调用哪一个默认构造函数。2.默认构造函数例:使用构造函数及默认构造函数,重新编写Circle类classCircle{private:
structpoint{intx,y;}center;
intradius;public:Circle(){center.x=0;center.y=0;radius=0;}Circle(int,int,int);
doublegetArea();
doublegetDistance();
voiddisplay();};例:使用构造函数及默认构造函数,重新编写Circle类//用户自定义的默认构造函数//构造函数Circle::Circle(intx,inty,intr){center.x=x;center.y=y;radius=r;}//构造函数的定义circle.cpp
voidmain(){Circlec1;c1.display(); cout<<"thedistancefromc1to0:"<<c1.getDistance()<<endl;cout<<"theAreaofc1:"<<c1.getArea()<<endl;Circlec2(3,4,1);c2.display(); cout<<"thedistancefromc2to0:“<<c2.getDistance()<<endl;cout<<"theAreaofc2:"<<c2.getArea()<<endl;}例:使用构造函数及默认构造函数,重新编写Circle类//调用默认构造函数//调用构造函数,注意调用格式对象数组是指每一个数组元素都是对象的数组。也就是说,若某一个类有若干个对象,我们可以把这一系列被创建的对象用一个数组来存放。当说明一个对象数组时,编译器会为这个对象数组的每个元素对象调用一次默认构造函数来初始化每个元素对象。
【例3.10】对象数组13.对象数组
65
对象数组的初始化
当对象数组所属类含有带参数的构造函数时,(1)可用初始化列表按顺序调用构造函数初始化对象数组的每个元素,如:
PointTriangle[3]={Point(0,0),Point(5,5),Point(10,0)};PointRectangle[2][2]={Point(0,0),Point(0,6),Point(16,6),Point(16,0)};【例3.11】对象数组2。
(2)也可以先定义后给每个元素赋值,其赋值格式为:
对象数组名[行下标][列下标]=构造函数名(实参表);例如:
Rectangle[0][0]=Point(0,0);Rectangle[0][1]=Point(0,6);
Rectangle[1][0]=Point(16,6);Rectangle[1][1]=Point(16,0);
67
voidmain(){Circlearray[2];array[0].display(); cout<<"thedistancefromc1to:“<<array[0].getDistance()<<endl;cout<<"theAreaofc1:"<<array[0].getArea()<<endl;array[1]=Circle(3,4,1);
array[1].display(); cout<<"thedistancefromc2to0:“<<array[1]=.getDistance()<<endl;cout<<"theAreaofc2:"<<array[1]=.getArea()<<endl;}例:在Circle类的main函数中使用对象数组//调用两次默认构造函数,对两个数组元素初始化//调用构造函数,注意调用格式//定义对象数组课下练习:再增加一个实例化圆,并计算二、三两圆心之间的距离。拷贝构造函数的功能是用一个已有的对象来初始化一个被创建的同类对象。例:用构造函数创建d1,并用d1初始化新创建的对象d2。Tdated1(3,1,2002);Tdated2(d1);拷贝构造函数是一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类对象的引用,它的特殊功能是将参数代表的对象逐域拷贝到新创建的对象中。拷贝构造函数的声明形式为:
类名(类名&对象名);4.拷贝构造函数外联拷贝构造函数定义的格式如下:
<类名>::<拷贝构造函数名>(<类名>&<引用名>){<函数体>}
其中,<拷贝构造函数名>与该类名相同;用户可以根据自己实际问题的需要定义特定的拷贝构造函数,以实现同类对象之间数据成员的传递。如果用户没有声明类的拷贝构造函数,系统就会自动生成一个默认拷贝构造函数,这个默认拷贝构造函数的功能是把初始对象的每个数据成员的值都复制到新建立的对象中。4.拷贝构造函数
classcat{
private:
intage;
floatweight;
char*color;
public:cat();cat(cat&);voidplay();
voidhunt();};cat::cat(cat&other){age=other.age;weight=other.weight;color=other.color;}//默认构造函数//拷贝构造函数的声明//拷贝构造函数的定义(外联)例①用类的一个对象初始化另一个对象catcat1;
catcat2(cat1);
//创建cat2时系统自动调用拷贝构造函数,用cat1初始化cat2。②用类的一个对象初始化另一个对象时的另外一种形式catcat2=cat1;
//注意并非catcat1,cat2;cat2=cat1;③对象作为函数参数传递时,调用拷贝构造函数。f(cata){}
//定义f函数,形参为cat类对象catb;
//定义对象bf(b);
//进行f函数调用时,系统自动调用拷贝构造函数1、关于自动调用拷贝构造函数的四种情况④如果函数的返回值是类的对象,函数调用返回时,调用拷贝构造函数。#include<iostream>usingnamespacestd;classCExample{private:inta;public:CExample(intb){a=b;}
voidshow(){cout<<a<<endl;}};从以上代码的运行结果可以看出,系统为对象B分配了内存并通过系统提供的默认拷贝构造函数完成了与对象A的复制过程。voidmain(){CExampleA(100);CExampleB(A);B.show();}#include<iostream>usingnamespacestd;classCExample{
private:inta;
public:CExample(intb){a=b;}
CExample(constCExample&C){a=C.a;}
voidshow(){cout<<a<<endl;}};voidmain(){CExampleA(100);CExampleB=A;B.show();}CExample(constCExample&C)是自定义的拷贝构造函数。函数的名称和类名一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。2、关于浅拷贝和深拷贝int*p1=newint(1);int*p2=p1;
deletep1;cout<<"V_p1:"<<*p1<<endl;cout<<"V_p2:"<<*p2<<endl;
cout<<"D_p1:"<<p1<<endl;cout<<"D_p2:"<<p2<<endl;deletep2; //浅拷贝:只有p1的值被复制到p2,结果是两个指针指向堆中的同一块空间。//由于p1被删除,*p1和*p2均为无意义的值。//p1和p2悬空,但是其值依然存在。//由于p1被删除,p2就不能再被删除,否则同一空间被释放两次,会出现运行时错误。2、关于浅拷贝和深拷贝int*p1=new
int(1);int*p2=p1;
deletep1;cout<<"V_p1:"<<*p1<<endl;cout<<"V_p2:"<<*p2<<endl;
cout<<"D_p1:"<<p1<<endl;cout<<"D_p2:"<<p2<<endl;deletep2; //深拷贝:复制整个对象,两个指针p1和p3都分别指向堆中自己唯一的一块空间。//p1被删除,对p3无影响,*p3仍然存在。//p1和p2地址值相同,但p3是独立的唯一的地址。//释放p3是正确的。int*p3=new
int(*p1);cout<<"V_p3:"<<*p3<<endl;cout<<“D_p3:"<<p3<<endl;deletep3;3、关于深拷贝构造函数和浅拷贝构造函数【例3.12】深拷贝构造函数
70拷贝构造函数分为两种,深拷贝和浅拷贝构造函数。由C++提供的默认拷贝构造函数只是浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源(只对对象中的数据成员进行简单赋值)。一般情况下,只需使用系统提供的浅拷贝构造函数即可,但是如果对象的数据成员包括指向堆空间的指针,就不能使用这种拷贝方式,因为两个对象都拥有同一个资源,对象析构时,该资源(动态申请的空间)将经历两次资源返还,从而出现错误。此时必须自定义深拷贝构造函数,为创建的对象分配堆空间,否则会出现动态分配的指针变量悬空的情况。深拷贝需要同时复制对象空间和资源。<类名>::<构造函数>(<参数表>):<变量1>(<初值1>),…,<变量n>(<初值n>){…}4、关于数据成员的初始化列表数据成员的初始化列表,也称为构造函数初始化列表,用于初始化(而不是赋值,请记住:初始化要优于赋值)类的非静态数据成员(一般变量)。这个列表只在构造函数定义中有效,必须立即出现在封闭形参列表的圆括号后。Circle(){center.x=0;center.y=0;radius=1;}Circle():radius(1){center.x=0;center.y=0;}例对于类的数据成员是一般变量的情况,放在冒号后面与放在函数体中初始化都一样。4、关于数据成员的初始化列表常量和引用的初始化必须使用这种初始化列表方式。如果类有多个构造函数,那么每个构造函数都必须初始化所有的常量及引用。对于常量和引用变量,除了通过这种方式进行初始化之外,别无选择。
class
Circle{
private:
structpoint{intx,y;}center;
intradius;
constdoublePI;
public:Circle():radius(1),PI(3.1415926) {center.x=0;center.y=0;}Circle::Circle(intx,inty,intr):PI(3.1415926) {center.x=x;center.y=y;radius=r;}……};errorC2758:'PI':mustbeinitializedinconstructorbase/memberinitializerlist4、关于数据成员的初始化列表常量和引用的初始化必须使用这种初始化列表方式。如果类有多个构造函数,那么每个构造函数都必须初始化所有的常量及引用。对于常量和引用变量,除了通过这种方式进行初始化之外,别无选择。例3.13对常量和引用数据成员初始化。
72(1)使用转换构造函数实现类型转换通过转换构造函数用作类型转换可以实现基本数据类型转换为类类型。转换构造函数一定要带有一个(且只有一个)基本数据类型的参数,这样才可以实现从参数类型向该类类型的转换,并且这种转换是隐式的。所谓隐式,是指这个转换动作是由编译器来完成的,不需要程序员提供一个明确的操作。5、类类型和基本数据类型的转换Person(strings);基本类型类类型classPerson{public:Person(stringn){name=n;}
voidprintname(){cout<<name<<endl;}private:stringname;};voidf(Persons){s.printname();}voidmain(){Personp1("zhang");f(p1);stringp2="wang";f(p2);}5、类类型和基本数据类型的转换//转换构造函数string->Person//注意:f函数的参数类型为Person类//不需类型转换,p1本身既为Person类型//隐式调用转换构造函数,将p2转换为Person类型//调用构造函数这种隐式转换的确为我们提供了方便,但有时也会导致一些无法预料的错误,而这些错误往往细微得难以察觉。这时候,我们宁愿关闭这种隐式类型转换动作,以保证程序的正确性。C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。使用explicit的好处是:将难以察觉的、后果严重的运行期错误变成了容易改正的编译期错误。classPerson{public:Person(stringn,inti){name=n;id=i;}Person(stringn){name=n; }
voidprint(){ cout<<name<<","<<id<<endl;}private:stringname;intid;};voidf(Personp){
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《中药学(第2版)》课件24- 补阴药 补血药
- 安全施工协议书15篇
- 2026年上海市杨浦区八年级语文下学期期中考试试卷及答案
- 钢结构伸缩缝处理施工工艺流程
- 2026年校园食品安全管理制度及规范
- 接待资料收集管理规定
- 生产现场叉车等搬运设备安全操作自查报告
- 患者气管插管意外滑脱应急演练脚本流程及总结
- 水利工程安全管理制度
- 南京市辅警招聘笔试题及答案
- 中信兴业投资集团2026届校园招聘笔试历年典型考点题库附带答案详解
- DB32-T 5389-2026 太阳能光伏与建筑一体化应用技术规程
- 26年ap化学2025真题及答案
- 2026陕西省为县以下医疗卫生机构定向招聘医学类毕业生招聘607人农业笔试参考题库及答案解析
- 2026年中考时政热点综合分析学案(含答案)
- AQ 3026-2026《化工企业设备检修作业安全规范》全面解读
- 2026中级社工《综合能力》高分通关卷5
- 水利工程安全文明措施费用分解
- 重庆水务集团招聘真题
- 2026民用航空器维修执照考试题库
- 基层脑卒中防治中心建设与管理指南
评论
0/150
提交评论