版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Object-Oriented
Programming
inC++
第三章再论类和对象3.1构造函数3.2析构函数3.3调用构造函数和析构函数的顺序3.4对象数组3.5对象指针3.6共用数据的保护3.7对象的动态建立和释放3.8对象的赋值和复制3.9静态成员3.10友元3.11类模板3.1构造函数对象的初始化 和普通变量一样,我们定义一个变量,往往同时进行初始化:
inta=3;
而声明类时,数据成员进行初始化:
classtime {inthour=0;
intminute=0;
intsecond=0; }
?3.1构造函数 定义对象时(而不是声明类时)进行初始化:
classtime {public/private:
inthour;
intminute;
intsecond; }; timet1={13,30,20};
3.1构造函数构造函数的作用
C++提供了构造函数(constructor)来处理对象的初始化。构造函数是一个由用户定义的特殊的成员函数。与其他成员函数不同之处在于:用户不能调用它,而是在定义对象时,有系统自动调用构造函数。构造函数的名字必须与类名一致,不能是其他名字。构造函数不能有任何返回类型。用户如果没有定义构造函数,系统会自动生成一个构造函数,只不过函数体中没有任何语句。3.1构造函数例将前例的时间类定义构造函数。在构造函数中加入输出语句,看看运行效果。classtime{private:
inthour,minute,second;public:
time()//time类的构造函数
{hour=0;minute=0;second=0;}voidsetTime();voidshowTime(){cout<<hour<<“:”<<minute<<“:”<<second<<endl;}};voidtime::setTime(){cout<<“hour=“<<endl;cin>>hour;
cout<<“minute=“<<endl;in>>minute;
cout<<“second=“<<endl;cin>>second;}voidmain(){timet1;//定义time类对象t1,调用构造函数tume()t1.setTime();t1.showTime();timet2;//定义time类对象t2,调用构造函数tume()t2.setTime();t2.showTime();}3.1构造函数类外定义构造函数classTime{public:Time();voidshow_time();private:
inthour;
intminute;
intsec;};
Time::Time(){hour=0;minute=0;sec=0;}
voidt1.time()Cout<<“构造函数”;3.1构造函数带参数的构造函数
声明一个构造函数的一般格式为: 构造函数名(类型1形参1,类型2形参2,…);定义一个对象的一般格式为: 类名对象名(实参1,实参2,…);3.1构造函数例3.2:有两个长方体,长宽高分别为(1,2,3)和(4,5,6)。试编写一基于对象的程序,分别求他们的体积,并且要求用带参数的构造函数初始化他们。#include<iostream.h>classbox{private:
intheight,width,length;public:
box(inth,intw,int
len){height=h;width=w;length=len;}
intvolume(){returnheight*width*length;}};voidmain(){boxbox1(1,2,3);
cout<<“box1的体积为”<<box1.volume()<<endl;boxbox2(4,5,6);
cout<<“box2的体积为”<<box2.volume()<<endl;}改写为类外定义该构造函数P723.1构造函数用参数初始化表对数据成员初始化 这种方法不在函数体内初始化数据成员,而是在函数首部实现。#include<iostream.h>classbox{private:
intheight,width,length;public:
box(int
h,int
w,int
len):height(h),width(w),length(len){}
intvolume(){returnheight*width*length;}};voidmain(){boxbox1(1,2,3);
cout<<“box1的体积为”<<box1.volume()<<endl;boxbox2(4,5,6);
cout<<“box2的体积为”<<box2.volume()<<endl;}3.1构造函数 示例中的初始化表表示,用形参h的值初始化数据成员height,用w值初始化width,用len值初始化length。这种初始化方法比较简练,可以直接在类体中定义构造函数。box(inth,intw,intlen):height(h),width(w),length(len){}3.1构造函数构造函数的重载 一个类中,可以有多个构造函数,只要他们的参数表不同。以方便同类对象不同初始化的需要。见下例
:#include<iostream>usingnamespacestd;classBox{public:Box();
Box(int
h,intw,int
len):height(h),width(w),length(len){}
intvolume();private:
intheight;
intwidth;
intlength;};Box::Box(){height=10;width=10;length=10;}int
Box::volume(){return(height*width*length);}
intmain(){Boxbox1;
cout<<"Thevolumeofbox1is"<<box1.volume()<<endl;Boxbox2(15,30,25);
cout<<"Thevolumeofbox2is"<<box2.volume()<<endl;return0;}构造函数的重载
#include<iostream.h>classcircle{private:floatradius;public://声明一个无参数构造函数//声明一个带参数构造函数floatarea(){returnradius*radius*3.14159;}};voidmain(){circlec1(1.0),c2;
cout<<“c1的面积为”<<c1.area()<<endl;
cout<<“c2的面积为”<<c2.area()<<endl;}3.1构造函数构造函数的重载
#include<iostream.h>classcircle{private:floatradius;public:
circle();
//声明一个无参数构造函数circle(floatr):radius(r){}//声明一个带参数构造函数floatarea(){returnradius*radius*3.14159;}};circle::circle(){radius=10.0;}voidmain(){circlec1(1.0),c2;
cout<<“c1的面积为”<<c1.area()<<endl;
cout<<“c2的面积为”<<c2.area()<<endl;}3.1构造函数说明参数表为空的构造函数叫默认构造函数,一个类中只能有一个默认函数。
定义对象时,如果想用默认构造函数初始化它,正确的定义形式为:
circlec2;
而不是:
circlec2();
一个类尽管定义了多个构造函数,一个对象只能用其中一个来初始化。
3.1构造函数使用默认参数的构造函数 构造函数的参数既可以通过实参传送,也可以指定为某些默认值。当用户不指定实参值时,编译系统便将默认值为形参值。 采用默认值,可以减少用户的输入量。
3.1构造函数使用默认参数的构造函数#include<iostream>usingnamespacestd;classBox{public:
Box(intw=10,inth=10,intlen=10);
intvolume();private:
intheight;
intwidth;
intlength;};Box::Box(int
w,int
h,int
len){height=h;width=w;length=len;}int
Box::volume(){return(height*width*length);}intmain(){Boxbox1;
cout<<"Thevolumeofbox1is"<<box1.volume()<<endl;Boxbox2(15);
cout<<"Thevolumeofbox2is"<<box2.volume()<<endl;Boxbox3(15,30);
cout<<"Thevolumeofbox3is"<<box3.volume()<<endl;Boxbox4(15,30,20);
cout<<"Thevolumeofbox4is"<<box4.volume()<<endl;return0;}使用默认参数的构造函数
#include<iostream.h>classcircle{private:floatradius;public://声明构造函数指定默认参数floatarea(){returnradius*radius*3.14159;}};//定义函数voidmain(){circlec1(10.0),c2;
cout<<“c1的面积为”<<c1.area()<<endl;
cout<<“c2的面积为”<<c2.area()<<endl;}3.1构造函数使用默认参数的构造函数#include<iostream.h>classcircle{private:floatradius;public:
circle(floatr=1.0);
//声明构造函数指定默认参数floatarea(){returnradius*radius*3.14159;}};circle::circle(floatr)//定义函数时,可不再指定默认参数{radius=r;}voidmain(){circlec1(10.0),c2;
cout<<“c1的面积为”<<c1.area()<<endl;
cout<<“c2的面积为”<<c2.area()<<endl;}3.1构造函数构造函数中使用默认参数的好处提供建立对象时的多种选择,相当于好几个重载的构造函数。即使在调用构造时不提供参数也不会出错,因为有默认参数值参与对象初始化。当每一个对象都是相同的初始值时,非常方便,用户不需要输入数据。3.1构造函数默认参数值的构造函数使用注意何处指定默认参数值?构造函数的声明处还是定义处?应该在构造函数的声明处指定默认参数值。因为类的声明在头文件中,用户是看得见的,而在函数的定义处,用户不一定看得见。一个类定义了全部是默认参数的构造函数后,不能再定义重载的构造函数。否则会产生多义性,系统不知道调用哪一个。例如一个类有右边形式的三个重载构造函数,若定义了如下对象: circlecircle1(); 它调用哪一个构造函数呢?系统不能确定,从而引起混乱。circle(floatr=2.3);circle();circle(float);3.2析构函数什么是析构函数? 析构函数(destructor)也是一个特殊函数,它的作用与构造函数相反,是在撤消对象占用的内存前进行一些清理工作。析构函数只能有一个。 析构函数的名称是类名的前面加一个取反符号“~”。我们在类的声明中定义析构函数。如果用户不定义析构函数,系统便自动生成一个空的析构函数。析构函数特点:没有返回类型;没有函数参数;不能被重载。3.2析构函数什么时候运行析构函数? 当对象的生命结束时,会自动执行它的析构函数。具体而言,当出现以下几种情况,析构函数就会被执行。如果在函数中定义了一个对象,当函数调用结束时,释放对象,释放对象前自动执行析构函数。
static局部对象在函数调用结束时,包含的对象不会被释放,只在main函数结束或调用exit函数时,才调用static局部对象的析构函数。如果定义了一个全局对象,,则在程序的流程离开其作用域时(如main函数结束,或exit语句),调用该全局对象的析构函数。如果用new运算符动态地建立了一个对象,当用delete运算符释放对象时,先调用该全局对象的析构函数。3.2析构函数#include<iostream>#include<string>usingnamespacestd;classStudent{public:
Student(int
n,string
nam,chars){num=n;name=nam;sex=s;
cout<<"Constructorcalled."<<endl;}
~Student(){cout<<"Destructorcalled."<<endl;}voiddisplay(){cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl<<endl;}private:
intnum;stringname;charsex;};intmain(){Studentstud1(10010,"Wang_li",'f');stud1.display();Studentstud2(10011,"Zhang_fun",'m');stud2.display();return0;}析构函数使用
#include<iostream.h>classbox{private:
intheight,width,length;public:
box(inth,intw,int
len){height=h;width=w;length=len;}//析构函数intvolume(){returnheight*width*length;}};voidmain(){boxbox1(1,2,3);
cout<<“box1的体积为”<<box1.volume()<<endl;boxbox2(4,5,6);
cout<<“box2的体积为”<<box2.volume()<<endl;}3.2析构函数#include<iostream.h>classbox{private:
intheight,width,length;public:
box(inth,intw,int
len){height=h;width=w;length=len;}~box()//析构函数{cout<<“Destructoringaobject”<<endl;}
intvolume(){returnheight*width*length;}};voidmain(){boxbox1(1,2,3);
cout<<“box1的体积为”<<box1.volume()<<endl;boxbox2(4,5,6);
cout<<“box2的体积为”<<box2.volume()<<endl;}析构函数使用3.3调用构造函数和析构函数的顺序调用构造函数和析构函数的顺序 先构造的后析构,后构造的先析构。#include<iostream.h>classbox{private:
intheight,width,length;public:
box(inth,intw,int
len){height=h;width=w;length=len;
cout<<“Constructoraobject”<<endl;}
~box()//析构函数{cout<<“Destructoringaobject”<<endl;}
int
vol(){returnheight*width*length;}};voidfn(){boxb1(2,2,2);
cout<<“b1=”<<b1.vol()<<endl;
staticboxb2(3,3,3);cout<<“b2=”<<b2.vol()<<endl;}voidmain(){fn();boxb3(1,2,3);
cout<<“b3=”<<b3.vol()<<endl;boxb4(4,5,6);
cout<<“b4=”<<b4.vol()<<endl;}//fn结束不析构b2,直到main结束3.4对象数组数组不仅可以由简单变量组成,也可以由对象组成,即每一个数组元素都是同类的对象。#include<iostream>usingnamespacestd;classBox{public:
Box(inth=10,intw=12,intlen=15):
height(h),width(w),length(len){}
intvolume();private:
intheight;
intwidth;
intlength;};int
Box::volume(){return(height*width*length);}
intmain(){Boxa[3]={Box(10,12,15),Box(15,18,20),Box(16,20,26)};
cout<<"volumeofa[0]is"<<a[0].volume()<<endl;
cout<<"volumeofa[0]is"<<a[1].volume()<<endl;
cout<<"volumeofa[0]is"<<a[2].volume()<<endl;return0;}3.4对象数组 数组不仅可以由简单变量组成,也可以由对象组成,即每一个数组元素都是同类的对象。 例如,一个班有30人,每个学生的属性包括学号、姓名、性别。我们建立起学生类后,为每个学生建立一个对象,需要分别取30个对象名,很不方便。较好的做法是,定义一个“学生类”,的对象数组,每一个数组元素是一个“学生类”的对象。
用指定参数的构造函数初始化数组#include<iostream.h>#include<string.h>classstudent{private:
intnum;charname[10];charsex;public:student(intn,stringnam,chars){num=n;
strcpy(name,nam);sex=s;
cout<<“Constructorcalled.“<<endl;}~student(){cout<<“Destructorcalled.”<<endl;}voiddisplay();};voidstudent::display(){cout<<“num:”<<num<<endl;
cout<<“name:”<<name<<endl;
cout<<“sex:”;if(sex==0){cout<<“男”<<endl;}else{cout<<“女”<<endl;}}voidmain()//用指定参数的构造函数初始化数组
cout<<“第一个学生";stud[0].display();
cout<<“第二个学生";stud[1].display();
cout<<“第三个学生";stud[2].display();}3.4对象数组#include<iostream>#include<string>usingnamespacestd;classstudent{private:
intnum;stringname[10];charsex;public:student(intn,stringnam,chars){num=n;name=nam;sex=s;
cout<<“Constructorcalled.“<<endl;}~student(){cout<<“Destructorcalled.”<<endl;}voiddisplay();};voidstudent::display(){cout<<“num:”<<num<<endl;
cout<<“name:”<<name<<endl;
cout<<“sex:”;if(sex==0){cout<<“男”<<endl;}else{cout<<“女”<<endl;}}voidmain(){studentstud[3]={student(1001,“张三",1),student(1002,“李四",0),student(1003,“王五",0)};//用指定参数的构造函数初始化数组
cout<<“第一个学生";stud[0].display();
cout<<“第二个学生";stud[1].display();
cout<<“第三个学生";stud[2].display();}3.5对象指针指向对象的指针 创建一个类的对象时,系统会为每一个对象分配一定的存储空间,以存放成员。对象空间的起始地址就是对象的指针。可以定义一个指针,用来存放对象的指针:访问成员的方法:(*pt).hour,(*pt).put()
或者:pt->hour,pt->put()classTime{public:
inthour,minute,sec;voidput(){hour=12;minute=0;sec=0;}};voidmain(){Time*pt,t1;pt=&t1;p1.put();
cout<<pt->hour<<“:”<<pt->minute<<“:”<<sec<<endl;
cout<<(*pt).hour<<“:”<<(*pt).minute<<“:”<<(*pt).sec<<endl;}3.5对象指针指向对象数据成员的指针 定义一个指向对象数据成员的指针变量的方法和前面介绍的定义指向普通变量的指针变量方法相同。
定义格式:数据类型名*指针变量名;例如:
int*pl;//定义指向整形数据的指针变量
pl=&t1.hour;//将t1的数据成员hour地址赋给指针pl,使其指向t1.hour
cout<<*pl<<endl;//输出t1.hour的值
3.5对象指针指向对象成员函数的指针 定义指向对象成员函数的指针变量的方法和定义指向普通函数的指针变量的方法有所不同。
指向普通函数的指针变量的定义方法:
返回数据类型(*指针变量名)(参数表列);例:void(*p)();//p是指向void类型函数的指针变量
p=fun;//将fun函数的入口地址赋给指针变量p(*p)();//调用fun函数而定义一个指向对象成员函数的指针变量则不能这样:
p=t1.put;//出现编译错误,不知道t1.put所属的类3.5对象指针定义指向公用成员函数的指针变量方法:
返回数据类型(类名::*指针变量名)(参数表列);例:void(Time::*p2)();//Time::*p2的括号必须加上令指针变量指向一个公用成员函数的方法: 指针变量名=&类名::成员函数名;例:p2=&Time::put;3.5对象指针#include<iostream.h>classTime{public:
inthour,minute,sec;Time(inth,intm,ints){hour=h;minute=m;sec=s;}voidget_Time(){cout<<hour<<“:”<<minute<<“:”<<sec<<endl;}};voidmain(){Timet1(10,12,56);
int*pl=&t1.hour;//定义指向整形数据的指针,指向t1.hour
cout<<*p1<<endl;t1.get_Time();//调用t1的成员函数Time*p2=&t1;
//定义指向Time类对象的指针变量t2,并指向t1p2->get_Time();//调用p2所指对象的成员函数void(Time::*p3)();
//定义指向Time类公用成员函数的指针变量p3p3=&Time::get_Time;
//使p3指向Time类公用成员函数get_Time()(t1.*p3)();//调用p3所指的成员函数t1.get)Time()}3.5对象指针this指针 通过第二章的学习我们知道,多个同类对象在内存中是共享成员函数的,以节省内存空间。
C++在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,成为“this”。它是指向本对象的指针,它的值是当前被调用的成员函数所在对象的起始地址。 例如,当a的成员函数调用数据成员a.volume时,编译系统就把对象a的起始地址赋给this指针,于是在成员函数引用数据成员时,就按照this的指向找到对象a的数据成员。3.5对象指针 比如下列涉及数据成员的运算的返回语句:
returnlength*width*height; 实际上C++处理为:
return(this->length)*(this->width)*(this->height); 也可以写成如下形式:
return((*this).length*(*this).width*(*this).height); 但不能写成:
return((*this.length)*(*this.width)*(*this.height));//错误 因为“.”操作符的优先级高于指针运算符“*”,(*this.length)就相当于*(this.length),而this.length
是不合法的,编译出错(应该是this->length)。3.6共用数据的保护
C++采用了不少的数据保护措施。最常用的,是将数据成员设置成私有数据(private),以增加数据的安全性和私密性。 但是,有时候要求数据在能共享的前提下能不能被篡改,我们就需要借助其他手段了。 什么手段呢?可以采用const,即把要保护的数据定义为常量。3.6共用数据的保护常对象 在定义对象时指定对象为常对象。常对象中的数据成员为常变量,并且必须要有初值:
Timeconstt1(12,34,56);
这样,在所有的场合中,对象t1的所有数据成员都被保护了,不能被修改。因此,凡是希望数据成员不能被改变的对象,可以声明为常对象。其声明格式为: 类名const对象名(参数表列); 或者:
const类名对象名(参数表列); 两者等价。3.6共用数据的保护 如果一个对象被定义成常对象,那么不能调用该对象的非const型成员函数,当然,系统隐含调用的构造函数和析构函数除外。 Timeconstt1(12,34,56);//定义t1为常对象 t1.get_Time();//错误,get_Tiem()不是const型,不能调用 为什么会这样?因为成员函数有可能修改数据成员,而成员函数的定义可能和成员函数的声明不在同一文件,系统没法检测。所以,系统只能统一拦截,不让用户调用常对象的成员函数,除非该成员函数被声明成const类型。3.6共用数据的保护 怎样才能引用常对象的成员函数呢?常成员函数:
函数返回类型函数名(参数表列)
const;
比如:voidget_Time()const;//将函数声明成const类型 常成员函数可以访问常对象中的数据成员,但仍然不准修改它们。
有时候编程要求必须修改某个常对象中的数据成员,如某个计数器count,ANSIC++对此做了特殊的处理,将该数据成员声明为mutable,如:
mutable
intcount; 这样,常对象的数据成员count,就可以用常成员函数来访问和修改了。3.6共用数据的保护常对象成员
常数据成员:其作用和用法与一般常变量相似,在类的声明中,用关键词const来声明常数据成员,例如:
constinthour; 注意:常数据成员的初始化,不能采用在构造函数中对常数据成员赋予初值的方法,只能通过构造函数的参数初始化表对常数据成员进行初始化。 在类外定义构造函数,初始化形式为:
Time::Time(inth):hour(h){} 在类中声明了某一个数据成员为常数据成员后,该类的所有对象中的该数据成员的值是不可改变的,但可以是不同的(由每个对象的参数初始化表决定)。3.6共用数据的保护
常成员函数
一般的成员函数可以引用本对象中的非const数据成员,也可以修改它们。但如果成员函数声明为常成员函数,则只能引用本对象中的数据成员,而不能修改它们。如
voidget_Time()const;//const位置在最后
const是函数类型的一部分,在声明函数和定义函数时都要用到const关键字。 常成员函数可以引用常数据成员,也可以引用非const数据成员;而常数据成员可以被常成员函数引用,也可以被非const成员函数引用。够乱的,见90页表总结。3.6共用数据的保护数据成员非const成员函数const成员函数非const数据成员可引用,可修改可引用,不可修改const数据成员可引用,不可修改可引用,不可修改常对象的数据成员不可引用,不可修改可引用,不可修改3.6共用数据的保护怎样使用常成员函数呢?类中如果一些数据成员需要保护,另一些数据成员不需保护,我们就将需要保护的数据成员声明为const,以保证其值不被改变。类中如果所有数据成员都需保护,可以将所有数据成员都声明成const,本对象的任何成员函数,都只能引用,不能改变它们。或者将这些数据成员所属的对象声明成const,只让本对象的const成员函数可引用,但不能改变。如果一个对象被定义成了常对象,只能调用其中的const成员函数,不能调用非const成员函数。常成员函数不能调用另一个非const成员函数3.6共用数据的保护对象的常引用 前面讲过,引用主要是用于函数调用,将改变后的变量值带回到被调用的函数外。 但如果不希望在函数中修改参数,可以把引用型形参定义成const型:
函数返回类型函数名(const形参类型&形参名); 则在函数中不能改变形参值,也就不能改变对应的实参值。什么时候使用常引用?使用常引用作为函数参数,既能保证数据安全,不被修改;调用函数时又能不必建立实参的拷贝,提高了程序运行效率,节省了内存空间。对象的常引用#include<iostream>usingnamespacestd;classTime{public:
Time(int,int,int);
inthour;
intminute;
intsec;};
Time::Time(int
h,int
m,ints){hour=h;minute=m;sec=s;}
voidfun(constTime&t){t.hour=18;}intmain(){Timet1(10,13,56);fun(t1);
cout<<t1.hour<<endl;return0;}3.7对象的动态建立和释放问题的提出:前面我们所学创建对象的方法都是静态的,它们在程序运行过程中所占用的内存空间不能被释放。比如,在一个函数中定义了一个对象,只有在函数结束时,该对象才能被释放。 有时候,人们希望对象的创建和释放是在程序运行时,由运行程序的人决定C++语言用new、delete这两个运算符来实现。内存的分配和释放,也用来实现对象的建立与撤消。3.7对象的动态建立和释放动态建立对象的方法: 如果已经定义了一个Box类,可以用new运算符动态地创建一个Box对象:
newBox;
Box*pt;//定义一个指向Box类对象的指针ptpt=newBox;//创建一个Box对象,将新对象的指针值赋给ptcout<<pt->height<<endl;3.7对象的动态建立和释放 用new动态创建的对象一般没有对象名,只能通过指针访问。 在执行new运算时,如果内存不足,则创建失败。大多数C++编译系统都让new返回一个0指针,表示内存不足,操作失败。动态撤消对象的方法:由new创建的对象不再需要时,可以由delete运算符释放。释放方法为:
deletept;析构函数调用?3.8对象的赋值与复制对象的复制问题的提出:有时需要多个完全相同的对象,可不可以用克隆的方法呢?
C++可以根据一个已知的对象快速地复制出多个完全相同的对象。比如:
Boxbox2(box1);
其作用就是对象的克隆,即用一个已知的对象box1复制出一个完全相同的新对象box2。对象复制的格式: 类名被克隆出来的新对象名(已有的对象名);3.8对象的赋值与复制 从上面的一般形式可以看出,复制对象是一种特殊的构造对象方法,其构造参数不是一般变量,而必须是一个对象!请看:
//复制构造函数
Box::Box(constBox&b) {height=b.height; width=b.width; height=b.height; }
复制构造函数也是构造函数,它只有一个参数,这个参数是本类已有对象,而且采用常引用形式,使参数不能被改变。复制构造函数的作用是将实参对象的各个数据成员值一一赋予给新的对象中对应的数据成员。3.8对象的赋值与复制
C++还使用另一种方便的对象复制形式,形式为:
类名目标对象名=源象名; 如:
Boxbox2=box1,box3=box2;对象的赋值与复制的不同点对象的赋值:是在已经存在的对象之间进行数据赋值,因此必须先定义,再赋值;参数表是一般变量对象的复制:从无到有地建立一个相同的新对象,参数只有一个,而且是已有的同类对象。两者形式的不同: 类名(形参表列);//普通构造函数的声明 类名(类名&对象名);//复制构造函数的声明3.8对象的赋值与复制intmain(){Boxbox1(15,30,25);
cout<<"Thevolumeofbox1is"<<box1.volume()<<endl;Boxbox2=box1,box3=box2;
cout<<"Thevolumeofbox2is"<<box2.volume()<<endl;
cout<<"Thevolumeofbox3is"<<box3.volume()<<endl;return0;}
intmain(){Boxbox1(15,30,25),box2;
cout<<"Thevolumeofbox1is"<<box1.volume()<<endl;box2=box1;
cout<<"Thevolumeofbox2is"<<box2.volume()<<endl;return0;}//赋值3.8对象的赋值与复制什么时候使用复制构造函数?程序中需要建立一个对象,并用另一个已知对象初始化它,系统自动调用复制构造函数;当函数的参数是类的对象时,系统自动调用复制构造函数;
voidmain() {Boxb1(12,13,15); func(b1);}当函数的返回值是类的对象,系统自动调用复制构造函数
Boxf() {Boxb1(12,14,16);
returnb1;}//返回值是Box类的对象 voidmain() {Boxb2;
b2=f();}//f函数返回Box类的临时对象,并将它赋值给b2
3.9静态成员 在C语言中,如果想在多个函数中共享一个变量值,我们一般用全局变量。但由于全局变量破坏了封装性,安全得不到保证,在C++中不提倡使用全局变量,我们可以使用静态的数据成员来达到这个目的。静态数据成员静态数据成员以
static关键字定义。例如:
classstudent {public:
intdisplay(); private:
static
intcount; charname[10];
intage; };3.9静态成员 将对象中的count数据成员定义成static型,它就被同一种类的各个对象所共有,而不只属于某一个对象。静态数据成员只占份内存空间,而不是各个对象个拥有一份内存空间!每个对象都可以引用这个静态数据成员。静态数据成员的值对各个对象都是一样的。如果改变它的值,则在各个对象中这个数据成员的值都同时改变。3.9静态成员说明:在为对象分配空间时,不分配静态数据成员的空间,因为它不属于任何对象。只要类中定义了静态数据成员,即使不定义对象,编译系统也要为静态数据成员开辟内存空间。C语言中,我们知道,如果在一个函数中定义了一个静态变量,在函数结束时该静态变量不被释放,并保留其值。静态数据成员也是这样,它不随对象的建立而分配空间,也不随对象的撤消而释放空间,其值也被保留。静态数据成员在程序被编译时就分配了空间,在程序结束时,才释放空间。静态数据成员可以被初始化,但只能在类体之外初始化:
数据类型类名::静态数据成员名=初值; 不必在初始化语句中加static关键字,不能用参数初始化表初始化静态数据成员:
student(intc,char*p,inta):count(c){}
//错误,count是静态数据成员3.9静态成员静态数据成员既可以通过类名引用,也可以通过对象名引用。#include<iostream>usingnamespacestd;classBox{public:
Box(int,int);
intvolume();staticintheight;
intwidth;
intlength;};Box::Box(int
w,int
len){width=w;length=len;}int
Box::volume(){return(height*width*length);}int
Box::height=10;intmain(){Boxa(15,20),b(20,30);
cout<<a.height<<endl;
cout<<b.height<<endl;
cout<<Box::height<<endl;
cout<<a.volume()<<endl;return0;}3.9静态成员#include<iostream.h>#include<string.h>classStudent{public:
staticintcount;
//静态数据成员
charname[40];
Student(char*pN="noname"){cout<<"Createonestudent"<<pN<<"\n";
strcpy(name,pN);
count++;
cout<<count<<endl;}~Student(){cout<<"destructonestudent“<<name<<"\n";
count--;
cout<<count<<endl;}};intStudent::count=0;
voidmain(){Students1("zhangsan");
cout<<"Student::count“<<Student::count<<endl;Students2("lisi");
cout<<"Student::count“<<Student::count<<endl;}3.9静态成员静态成员函数在类的定义中,成员函数前如果加了static限定词,该成员函数就成为静态成员函数。例:
staticintvolume();用途:静态成员函数的作用不是为了对象之间的沟通,主要是为了引用本类中的静态数据成员。它可以直接引用本类的静态数据成员。静态成员函数与普通成员函数的区别:静态成员函数没有this指针,由此决定静态成员函数不能访问本类中的非静态数据成员,除非用“对象名.非静态数据成员”的形式。
静态成员函数示例3.11
#include<iostream>usingnamespacestd;classStudent{public:
Student(int,int,int);voidtotal();staticfloataverage();private:
intnum;
intage;floatscore;staticfloatsum;staticintcount;};
Student::Student(int
m,int
a,ints){num=m;age=a;score=s;}voidStudent::total(){sum+=score;count++;}
floatStudent::average(){return(sum/count);}floatStudent::sum=0;int
Student::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<n;i++)
stud[i].total();
cout<<"Theaveragescoreof"<<n<<"studentsis"<<stud[0].average()<<endl;return0;}Student::average()3.9静态成员#include<iostream.h>#include<string.h>classStudent{protected:
staticintcount;
//静态数据成员
charname[40];public:
Student(char*pN="noname"){cout<<"Createonestudent"<<pN<<"\n";
strcpy(name,pN);
count++;
cout<<count<<endl;}~Student(){cout<<"destructonestudent“<<name<<"\n";
count--;
cout<<count<<endl;}
staticintnumber()//静态成员函数
{returncount;}//直接引用本类的静态数据成员};intStudent::count=0;
voidfn(){Students1("zhangsan");Students2("lisi");
cout<<Student::number()<<endl;
cout<<s1.number()<<endl;
cout<<s2.number()<<endl;}voidmain(){fn();
cout<<"Student::number“<<Student::number()<<endl;}3.10友元 类具有封装性,类中的私有数据只有通过该类的成员函数才可以访问。如果在程序中需要访问类的私有成员,就必须通过对象来调用类的成员函数,频繁调用成员函数将影响程序运行效率。 为解决上述问题,C++提供一种友元机制,友元可以不通过调用成员函数就可以直接访问类的私有数据,以提高程序运行效率。 友元机制在数据封装这堵不透明的墙上开了一个小孔,友元的使用要慎重。 友元可以是一个普通函数,可以是一个类的成员函数,也可以是一个类。#include<iostream>usingnamespacestd;classTime{public:
Time(int,int,int);
friendvoiddisplay(Time&);private:
inthour;
intminute;
intsec;};
Time::Time(int
h,int
m,ints){hour=h;minute=m;sec=s;}voiddisplay(Time&t){
cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl;}
intmain(){Timet1(10,13,56);display(t1);return0;}3.10友元友元成员函数:friend函数可以是另一个类中的成员函数。#include<iostream.h>classtwo;classone{private:
intvalue;public:
one(intv){value=v;}voidsettwo(two&ob);voidshow(){cout<<"classone'sprivatevalue=“<<value<<endl;}};classtwo{private:
intvalue;public:voidshow(){cout<<"classtwo'sprivatevalue=“<<value<<endl;}
friendvoidone::settwo(two&ob);};voidone::settwo(two&ob){ob.value=value;}voidmain(){oneob1(2);twoob2;ob1.settwo(ob2);ob1.show();ob2.show();}3.10友元友元类:
当说明一个类为另一个类的友元时,友元类中的所有成员函数都是另一个类的友元函数。例如:#include<iostream.h>classX{public:
friendclassY;//类Y是类X的友元类
voidset(inti
){x=i;}voiddisplay(){cout<<“x=”<<x<<“,”<<“y=”<<y<<endl;}private:
intx;staticinty;//静态数据说明};classY{public:Y(int
i,intj);voiddisplay();private:Xa;//数据成员为类X的对象};intX::y=10;//静态数据定义并初始化Y::Y(int
i,intj){a.x=i;X::y=j;}voidY::display(){cout<<“x=”<<a.x<<“,”<<“y=”<<X::y<<endl;}voidmain(){Xb;b.set(5);
b.display();Yc(6,9);
c.display();
b.display();}结果:x=5,y=10x=6,y=9x=5,y=93.11
类模板(ClassTemplates)为什么要使用类模板 如果要对功能相同、仅类的数据类型不同的各种情况,都重新定义一种新的类型,会产生较大的重复。例如:
classcompare_int{public:
compare_int(inta,intb){x=a;y=b;}
intmax(){return(x>y)?x:y;}private:
int
x,y;};classcompare_char{public:
compare_char(chara,charb){x=a;y=b;}charmax(){return(x>y)?x:y;}private:charx,y;};voidmain(){compare_intc1(5,6);
cout<<c1.max()<<endl;
compare_charc2(‘a’,‘f’);
cout<<c2.max()<<endl;}3.11
类模板(ClassTemplates)类模板的定义: 为解决这一问题,C++引进类模板的概念。我们在类的声明前先加一行模板关键字。它用一个通用参数T来替代不同的数据类型。类模板的定义格式为:template<class类型参数名>class<类名>{//类体说明}; 其中template是关键字;<class类型参数名>中可以有多个参数,其间用逗号分隔。使用类模板定义对象的: 类模板名<实际数据类型名>对象名(实参列表); 例如:下面是一个数组类模板的例子#include<iostream.h>template<classT>classcompare{public:compare(Ta,Tb){x=a;y=b;}
Tmax(){return(x>y)?x:y;}private:
T
x,y;};voidmain(){compare<int>cmp1(3,7);
cout<<cmp1.max()<<“是两个整数中最大的数”<<endl;
compare<char>cmp2(‘a’,’g’);
cout<<cmp2.max()<<“是两个字符中最大的字符”<<endl;
compare<float>cmp3(1.0,3.0);
cout<<cmp3.max()<<“是两个浮点数中最大的数”<<endl;}习题2习题3#include<iostream>usingnamespacestd;classDate{public:Date(int=1,int=1,int=2005);voiddisplay();private:intmonth;intday;intyear;};
Date::Date(intm,intd,inty):month(m),day(d),year(y){}习题6习题7-1#include<iostream>usingnamespacestd;classStudent{public:
Student(int
n,float
s):num(n),score(s){}voidchange(int
n
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《原位构筑超分子纳米材料的性能研究与生物应用》
- 《中弘股份强制退市原因及启示案例研究》
- 《基于微孔碳球-硒复合物的锂硒电池正极材料制备与性能研究》
- 2023年绍兴兰溪市交通建设投资集团有限公司招聘笔试真题
- 2024年度大型基础设施建设农民工劳务分包合同范本2篇
- 2024年甲醇燃料订购合同样本
- 2024年现代农业示范园:大棚设计与施工合同
- 元宵节的日记集合7篇范文
- 专业会议策划与执行合同
- 2024年电线电缆订购协议3篇
- 2023年中证数据招聘笔试真题
- 2024年农村公寓房屋买卖协议书参考样本3篇
- 2024年山东省政府采购专家入库考试真题(共五套 第一套)
- 木桶效应-课件
- 五年级数学(小数乘除法)计算题专项练习及答案汇编
- 初中济南版生物实验报告单
- 北京邮电大学《自然语言处理》2023-2024学年第一学期期末试卷
- 2024年全国《考评员》专业技能鉴定考试题库与答案
- 实验室安全准入学习通超星期末考试答案章节答案2024年
- 名画中的瘟疫史智慧树知到期末考试答案章节答案2024年上海健康医学院
- 中国竹编艺术智慧树知到期末考试答案章节答案2024年浙江广厦建设职业技术大学
评论
0/150
提交评论