第三章 类与对象(一)_第1页
第三章 类与对象(一)_第2页
第三章 类与对象(一)_第3页
第三章 类与对象(一)_第4页
第三章 类与对象(一)_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第三章类和对象(一)本章主要内容3.1类的构成3.2成员函数的声明3.3对象的定义与使用3.4构造函数与析构函数本章重点类的定义、对象的定义与使用及构造函数与析构函数本章难点拷贝构造函数的调用情况、深拷贝与浅拷贝本章所需学时:5学时

类构成了实现C++面向对象程序设计的基础,在C++面向对象程序设计中占据着核心地位。对象把数据和作用在这些数据上的操作组合在一起,是封装的基本单元。对象是类的实例,类定义了属于该类的所有对象的共同特性。从一般意义上讲,对象(Object)是现实世界中的客观事物。类是把具有相同属性的事物划分为一类,从而得出的抽象概念。类是一组性质相同的对象的程序描述,它由概括了一组对象共同性质的数据和函数组成。面向对象的程序设计中最基本的概念是对象,一般意义上的对象指的是一个实体的实例,在这个实体中包括了特定的数据和对这些数据进行操作的函数。对象的核心概念就是通常所说的“封装性”(encapsulation)、“继承性”(inheritance)和“多态性”(polymorphism)。

§3.1类的构成从结构到类结构是C的一种自定义的数据类型,它把相关联的数据元素组成一个单独的统一体。例如下面声明了一个日期结构:

structDate{

intyear;

intmonth;

intday;};

C语言中的结构存在一些缺点。例如,一旦建立了一个结构变量,就可以在结构体外直接修改数据。可见,在C结构中的数据是很不安全的,C结构无法对数据进行保护和权限控制。C结构中的数据与对这些数据进行的操作是分离的,没有把这些相关的数据和操作(通常用函数实现)构成一个整体进行封装,因此使程序的复杂性很难控制,维护数据和处理数据要化费很大的精力,使传统程序难以重用,严重影响了软件的生产效库。在C++中,引入了类的概念,它能克服C结构的这些缺点。C++语言中的类将数据和与之相关的函数封装在一起,形成一个整体,具有良好的外部接口,可以防止数据未经授权的访问,提供了模块间的独立性。类的构成

类的构成:类名、数据成员与成员函数按访问权限分为:三类(public,private与protected)

类的定义

class<类名>{[private]:

<私有数据成员和成员函数>;

pulbic:

<公有数据成员和成员函数>;

protected:<保护数据成员和成员函数>;

};其中,class是定义类的关键字。<类名>是一个标识符,用于惟一标识一个类。一对大括号内是类的说明部分,说明该类的所有成员。上面的结构改写为类的形式为:

classDate{public:voidsetDate(int

y,int

m,intd);voidshowDate();private:intyear;intmonth;intday;};声明了一个类Date,封装了有关数据和对这些数据操作,分别称为类Date的数据成员和成员函数说明:对一个具体的类来讲,类声明格式中的3个部分并非一定要全有,但至少要有其中的一个部分。一般情况下,一个类的数据成员应该声明为私有成员,成员函数声明为公有成员。这样,内部的数据结构整个隐蔽在类中,在类的外部根本就无法看到,使数据得到有效的保护,也不会对该类以外的其余部分造成影响,程序模块之间的相互作用就被降低到最小。private、protected和public出现的顺序与次数。数据成员可以是任何数据类型,但不能用externautoregister等关键词说明。在类的说明中不能对数据成员进行初始化,C++规定只能在类定义之后才能给数据成员赋初值。§3.2成员函数的声明成员函数的声明通常采用以下两种方式:将成员函数以普通函数的形式进行说明,在类声明中只给出成员函数的原型,而成

员函数体写在类的外部。这种成员函数在类外定义的一般形式是:返回类型类名::成员函数名(参数说明){

//函数体}

例如,以下是表示坐标点的类Coord的声明。classCoord{public: voidsetCoord(int,int);//设置坐标点

int

getX();//取x坐标点

int

getY();//取y坐标点private:

int

x,y;};voidCoord::setCoord(int

a,intb){x=a;y=b;}int

Coord::getX(){returnx;}int

Coord::getY(){returny;}说明:在所定义的成员函数名之间缀上类名,在类名和函数名之间应加上分隔符“::”;在定义成员函数时,对函数所带的参数,不但要说明它的类型,还要指出其参数名;在定义成员函数时,其返回类型一定要与函数原型中声明的返回类型匹配成员函数定义在类的内部,即定义为内置函数。其也有两种定义类的内置函数:隐式定义,就是直接将函数定义在类的内部显示定义,就是将其成员函数的定义定义在类的外部,但在成员函数定义前加上关键词“inline”,使其起到内置函数的作用如classpoint{intx,y;public:voidsetpoint(inta,intb);{x=a;y=b;}

int

getX();{returnx;}

int

getY();{returny;}};说明:使用inline说明内置函数时,必须使函数体和inline说明结合在一起,否则编译器将它作为普通函数处理。通常只有较短的成员函数才定义为内联函数,对于较长的成员函数最好作为一般函数处理。例如在前面所声明日期类Date中,考虑到成员函数showDate()的语句相当少,可以使用显式的方法将其说明为内联成员函数,而成员函数setDate)仍作为普通的成员函数处理。练习:某公司要建造一座砖混结构的楼房,给出该房的长、宽、层数。并按当前的市场价格计算盖楼房的造价。请设计出一个程序以实现该功能。§3.3对象的定义及引用类与对象关系类的概念对象是类的实际变量,类与对象间的关系,就如整型int和整型变量i的关系。类和整型int均代表的是一般的概念,而对象和整型变量i却是代表具体的东西。C++把类的变量称为类的对象。对象定义的方式在声明类的同时,直接定义对象,即在声明类的右花括号“}”后,直接写出该类的对象名。例如

classstudent{………}stud1,stud2;声明类后,在使用时在再定义对象,定义形式同一般变量的定义格式相同,如studentstud1,stud2;说明类是对象的样板,只有定义对象后,系统才为对象分配存储空间第一种方式声明的对象是全局对象对象中成员的访问对象对对象成员的引用,引用格式:对象名.数据成员或对象名.成员函数(实参表)#include<iostream.h>classCoord{private:

int

x,y;public: voidsetpoint(int

a,intb) {x=a;y=b;}

int

getx() {returnx;}

int

gety() {returny;}};voidmain(){Coordop1;int

i,j;op1.setpoint(1,2);//op1.point::setpoint(1,2);i=op1.getx();//i=op1.x;j=op1.gety();//j=op1.y;

cout<<"op1i="<<i<<"op1j="<<j<<endl;}#include<iostream.h>classDate{private:

intyear; intmonth; intday;public: voidsetDate(int

y,int

m,intd) {year=y; month=m; day=d;} voidshowDate() {cout<<year<<'.'<<month<<'.'<<day<<endl;}};voidmain(){Datedate1,date2;

cout<<"Date1setandoutput:"<<endl;date1.setDate(1998,4,28);cout<<date1.year<<"."<<date1.month<<"."<<date1.day<<endl;cout<<"Date1setandoutput:"<<endl;date2.setDate(2002,11,14);cout<<date2.year<<"."<<date2.month<<"."<<date2.day<<endl;}指向对象的指针的成员表示如下:

<对象指针名>-><成员名>或者

<对象指针名>-><成员名>(<参数表>) 前者用于表示数据成员,后者用于表示成员函数。这里的“->”是一个表示成员的运算符,它与前面介绍过的“.”运算符的区别是:“->”用来表示指向对象的指针的成员,而“.”用来表示一般对象的成员。 对于数据成员和成员函数,以下两种表示方式是等价的:

<对象指针名>-><成员名>与 (*<对象指针名>).<成员名>如接上例:Coord*op;op->setpoint(1,2)与(*op).setpoint(1,2)等价类成员的访问属性在详细介绍派生类之前,让我们归纳一下类成员的访问属性。类成员有3种访问属性:公有类型(public)、私有类型(private)和保护类型protected)。

1.公有类型说明为公有的成员不仅可以被类中成员函数访问;还可在类的外部,通过类的对象进行访问。

2.私有类型说明为私有的成员只能被类中成员函数访问,不能在类的外部,通过类的对象进行访问。

3.保护类型说明为保护的成员除了类本身的成员函数可以访问外,该类的派生类的成员也可以访问,但不能在类的外部,通过类的对象进行访问。这里需要指出的是,类成员对类对象的可见性与对类的成员函数的可见性是不同的。类的成员函数可以访问类的所有成员,没有任何限制,而类的对象对类的成员的访问是受类成员的访问属性的制约的。例如声明了以下一个类:

classSample{private:

inti; protected:

intj;public:

intk;

int

geti(){returni;}

int

getj(){returnj;}

int

getk(){returnk;} //...Samplea;

a.i;

a.j;

a.k;//...对象赋值语句当两个整型变量x和y,那么可以用y=x,就可以把x值赋给y。同类型的对象之间也可以进行赋值,当一个对象赋值给另一个对象时,所有的数据成员都会逐位拷贝。如Coordop1,op2;其中op1.setpoint(1,2);op2=op1;见例3.5说明:在使用对象赋值语句进行对象赋值时,两个对象的类型必须相同,如果对象的类型不同,编译时将出错。两个对象之间的赋值,仅仅使这些对象中数据成员相同,而两个对象仍是分离的。例3.5中的对象赋值是通过缺省的赋值运算符函数实现的。当类中存在指针时,使用缺省的赋值运算符函数进行对象赋值,可能会产生错误。类的作用域类的作用域:指在类声明中的一对花括号所形成的作用域。一个类的所有成员都在该类的作用域内,一个类的任何成员可以访问该类的其他成员。C++把类的所有成员都作为一个整体的相关部分。一个类的成员函数可以不受限制地访问该类的成员,而在类的外部,对该类的数据成员和成员函数的访问则要受到一定的限制,有时甚至是不允许的,这体现了类的封装功能。例子3.6可以帮助我们理解类的作用域。#include<iostrea.h>classmyclass{public:

inti; voidinit(int); voidshow(){cout<<"i="<<i<<endl;}}; voidmyclass::init(int

si){i=si;}

int

fun(){returni;}//非法voidmain(){myclassob;ob.init(5);ob.show();i=8;ob.show();}§3.4构造函数与析构函数构造函数和析构函数都是类的成员函数,但它们是特殊的成员函数,不用调用便自动执行,而且这些函数的名字与类的名字有关。构造函数构造函数的作用是:为对象分配空间;对数据成员赋初值;请求其他资源。构造函数的特殊性质:构造函数名与类名相同,构造函数可以有任意类型的参数,但不能具有返回类型。构造函数是一种用于创建对象特殊的成员函数,当创建对象时,系统自动调用构造函数,不能在程序中直接调用。一个类可以拥有多个构造函数(重载)例3.7#include<iostream.h>#include<math.h>classcomplex{private: doublereal; doubleimag;public:

complex(double

r,doublei)//定义在类外怎么定义?

{real=r;imag=i;} doubleabscomplex() { doublet; t=real*real+imag*imag; returnsqrt(t); }};利用构造函数创建对象的形式:1用构造函数直接创建对象其一般形式:类名对象名[(实参表)];这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数。2利用构造函数创建对象时,通过指针和new来实现。其一般语法形式:类名*指针变量=new类名[(实参表)]

含义同上。下列给出了第一种创建对象的形式。

#include<iostream.h>#include<math.h>classcomplex{private: doublereal; doubleimag;public:

complex(double

r,doublei) {cout<“constructing…”<<endl;real=r;imag=i;}voidsetpoint(int

m,intn){real=m;imag=n;} doubleabscomplex() { doublet; t=real*real+imag*imag; returnsqrt(t); }};voidmain(){ complexA(3,4);cout<<"absofcomplexA="<<A.abscomplex()<<endl; A.setpoint(6,8);cout<<"absofcomplexA="<<A.abscomplex()<<endl;}complex*A;A=newcomplex(3,4);cout<<"absofcomplexA="<<A.abscomplex()<<endl;A->setpoint(6,8);cout<<"absofcomplexA="<<A.abscomplex()<<endl;//将主程序改为第二种创建对象的方法

说明:构造函数的名字与类名相同构造函数没有返回类型,即不能说明函数的类型如果一个类没有定义构造函数,编译器会自动生成一个不带参数的默认构造函数,其格式如下:

<类名>::<默认构造函数名>()

{}构造函数可以不带参数

成员初始化表

在声明类时,不能在数据成员的声明中对数据成员进行初始化,对数据成员的初始化工作一般在构造函数中用赋值语句进行。但是对于常量类型与引用类型的数据成员,又不能在构造函数中用赋值语句直接赋值。那么怎么办呢?C++提供了用成员初始化表的置初值的方式。用成员初始化表可圆满地解决这一问题。即带有成员初始化表的构造函数的一般形式为:类名::构造函数([参数表])[:成员初始化表]{//构造函数体}

成员初始化表的一般形式为:数据成员名1(初始值1),数据成员名2(初始值2),…具体的例子见3-9#include<iostream.h>classA{public:

A(intx1):x(x1),rx(x),pi(3.14)//rx(x)相当于rx=x,{}//pi(3.14)相当于pi=3.14voidprint(){cout<<"x="<<x<<""<<"rx="<<rx<<""<<"pi="<<pi;}private:

intx;

int℞constfloatpi;};main(){Aa(10);

a.print();return0;}说明:如果需要将数据成员存放在堆中或数组中,则应在构造函数中使用赋值语句,即使构造函数有成员初始化表也应如此;类成员是按照它们在类里被声明的顺序进行初始化的,与它们在成员初始化表中无关。见例3.10#include<iostream.h>classD{public:

D(inti):mem2(i),mem1(mem2+1){

cout<<"mem1:"<<mem1<<endl;

cout<<"mem2:"<<mem2<<endl;}private:

intmem1;

intmem2;};voidmain(){Dd(15);}缺省参数的构造函数对于带参数的构造函数,在定义对象时必须给构造函数传递参数,否则构造函数将不被执行。如果只有在特殊情况下才需要改变它的参数值,这时将其定义称带缺省参数的构造函数。如classcomplex{private:doublereal;double

imag;public:complex(doublei=0.0,doublej=0.0){real=i;imag=j;}voidshow(){cout<<real<<““<<imag<<endl;}};main(){complexs1;complexs2(1.1);complexs3(1.1,2.2)…}析构函数析构函数的作用是进行清除对象,释放内存等。析构函数名与类名相同,只是在前面加符号“~”,析构函数没有参数和返回值。一个类中只可能定义一个析构函数,所以析构函数不能重载。撤销对象时,系统自动调用析构函数。如同默认构造函数一样,如果一个类没有定义析构函数,编译器会自动生成一个默认析构函数,其格式如下:

<类名>::~<默认析构函数名>()

{ }

默认析构函数是一个空函数。

析构函数在什么时候显式定义呢?#include<iostream.h>classDate{public:

Date(int

y,int

m,intd) {cout<<"constrcuting..."<<endl; year=y;month=m;day=d;} ~Date(){cout<<"destructing..."<<endl;} voidsetDate(int

y,int

m,intd) {year=y;month=m;day=d;} voidshowDate() {cout<<year<<'.'<<month<<'.'<<day<<endl;}private:

int

year;int

month;intday;};voidmain(){ Datedate1(1998,4,27);

cout<<"Date1output1:"<<endl; date1.showDate(); date1.setDate(2002,11,14);

cout<<"Date1output2:"<<endl; date1.showDate();}对前面的知识点通过下例子来巩固#include<iostream.h>#include<string.h>classStudent{public:

Student(char*name1,char*stu_no1,floatscorel);~Student();voidmodify(float

scorel);voidshow();private:char*name;char*stu_no;floatscore;};Student::Student(char*name1,stu_no1,floatscorel){name=newchar[strlen(name1)+1];strcpy(name,name1);

stu_no=newchar[strlen(stu_no1)+1];strcpy(stu_no,stu_no1);score=scorel;}Student::~Student(){delete[]name;delete[]stu_no;}voidStudent::modify(float

scorel){score=scorel;}voidStudent::show(){cout<<"\nname:"<<name;

cout<<"\nstu_no:"<<stu_no;

cout<<"\nscore:"<<score;}voidmain(){Studentstu1("liming","990201",90);//定义类student的对象stu1,调用stu1的构造

//函数,初始化对象stulstu1.show();stu1.modify(88);stu1.show();}缺省的构造函数和缺省的析构函数

如果一个类没有定义构造函数,编译器会自动生成一个不带参数的默认构造函数,其格式如下:

<类名>::<默认构造函数名>()

{}说明:对没有定义构造函数的类,其公有数据成员可以在主程序中用初始值表进行初始化。例3.14与定义变量类似。在用缺省的构造函数创建对象时,如果创建对象的是全局对象或静态对象时,则对象的所有数据成员都初始化为0或空,否则,对象的数据成员值是随机的;例3.15只要一个类定义了一个构造函数,系统将不再给它提供缺省的构造函数。分析下面程序的执行结果:#include<iostream.h>classDate{public:

Date(int

y,int

m,intd);//带有参数的构造函数

voidsetDate(int

y,int

m,intd);voidshowDate();private:

intyear;

intmonth;

intday;};Date::Date(int

y,int

m,intd){year=y;month=m;day=d;}voidDate::setDate(int

y,int

m,intd){year=y;month=m;day=d;}inlinevoidDate::showDate(){cout<<year<<"."<<month<<"."<<day<<endl;}voidmain(){Datedate1;date1.setDate(2002,11,14);date1.showDate();}说明:

在类中存在带缺省参数和没有参数的构造函时,可能会出现二义性。 使用构造函数的限制:不能被继承,不能说明为虚函数,不能显式调用,不能取构造函数的地址。重载构造函数构造函数可以像普通函数一样被重载,C++根据说明中的参数个数和类型选择合适的构造函数。若类A具有一个或多个构造函数,创建类A的对象时,C++会根据参数选择调用其中一个。见下例所示:

#include<iostream.h>classDate{public:Date();//无参数的构造函数

Date(int

y,int

m,intd);//带有参数的构造函数

voidshowDate();private:

intyear;

intmonth;

intday;};

Date::Date(){year=1998;month=4;day=28;}Date::Date(int

y,int

m,intd){year=y;month=m;day=d;}inlinevoidDate::showDate(){

cout<<year<<"."<<month<<"."<<day<<endl;}voidmain(){Datedate1;//声明类Date的对象date1,//调用无参数的构造函数

cout<<"Date1output:"<<endl;date1.showDate();//调用date1的showDate(),显示date1的数据

Datedate2(2002,11,14);//定义类Date的对象date2,//调用带参数的构造函数

cout<<"Date2output:"<<endl;date2.showDate();//调用date2的showDate()显示date2的数据

}拷贝构造函数它是一种特殊的构造函数,形参是本类对象的引用,用一个已经存在对象去初始化另一个同类的对象。特点:1、它也是一种构造函数,与类名相同,没有返回值类型。2、只有一个参数(同类对象的引用)3、每一类必须有一个拷贝构造函数。(用户可定义,系统缺省的)自定义拷贝构造函数一般形式Classname(classname&ob){//拷贝构造函数的函数体}关于自定义拷贝构造函数的使用见下例:#include<iostream.h>classCoord{public:

Coord(int

a,intb){x=a;y=b;

cout<<"Usingnormalconstructor\n";}

Coord(Coord&p){x=2*p.x;y=2*p.y;

cout<<"Usingcopyconstructor\n";}voidprint(){cout<<x<<" "<<y<<endl;}private:

int

x,y;};main(){Coordp1(30,40);Coordp2(p1);p1.print();p2.print();return0;}缺省的拷贝构造函数在类中并没有定义拷贝构造函数,C++会自动将一个已存在的对象复制给新对象,按成员逐一复制的过程是由缺省拷贝函数自动完成。例3.20(见上例)说明与一般构造函数一样,拷贝构造函数没有返回值类型若类中有指针类型时,按成员复制的方法有时会产生错误调用拷贝构造函数的3种情况:

1、当明显地用一个对象初始化同类的另一个对象时

2、当函数的形参是类的对象时,调用函数,进行形参和实参的结合时。

3、当函数的返回值是对象时

//…

Coordfun(){Coordp1(30,40);returnp1;}main(){Coordp2;p2=fun();return0;}通过下例来演示拷贝构造函数的3种情况:#include<iostream.h>classCoord{public:

Coord(inta=0,intb=0);

Coord(const

Coor

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论