大二6系下cc第四章_第1页
大二6系下cc第四章_第2页
大二6系下cc第四章_第3页
大二6系下cc第四章_第4页
大二6系下cc第四章_第5页
已阅读5页,还剩41页未读 继续免费阅读

付费下载

下载本文档

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

文档简介

C++与C#计算机学院 李虎新主楼G910电话:82338574Email:2021/7/51第三章基于对象的程序设计(续)2021/7/52Student *

ps1

=

new

Student

(“lisi”,

3,17);……2021/7/53delete

ps1;//ps1->name并没有删除3.6

析构函数析构函数是构造函数的互补,为生命期即将结束的类对象自动释放资源。析构函数也是隐式自动调用的。注意:析构函数的名字是在类名前加上波浪线~,它不返回任何值也没有任何参数。尽管我们可以为一个类定义多个构造函数,但是我们只能提供一个析构函数,它将被应用在类的所有对象上。2021/7/54class

Student{public:Student(const

char

*

str,

int

i,

int

a=18);Student

();Student

(const

Student

&

other);~Student();……}Student::~Student(){delete

[]name;}void

main()st1(“lisi”,

1,17);*st2

=

new

Student(“zhangsan”,

2,

19);StudentStudent……delete

st2;……}st1

{class

Student{public:……int

getAge()

{ return

age;

}int

setAge(inta){ age=a;}…private:char

*name;int

id;int

age;}const

Student

st1(“lisi”,

1,

18);int

age

=st1.getAge();……st1.setAge(20);?3.7

const成员函数只有被声明为const的成员函数才能被一个const类对象调用,关键字const被放在成员函数的参数表和函数体之间,对于在类体之外定义的const成员函数,我们必须在它的定义和声明中同时指定关键字const2021/7/55class

Student{public:……int

getAge()

const

{ return

age;

}int

setAge(int

a)

{ age

=a;

}int

getId

()

const;…private:char

*name;int

id;int

age;}int

Student::getId()

const{return

id;}const

Student

st1(“lisi”,

1,

18);int

age

=st1.getAge();int

id

=

st1.getId();……int

setAge(int

a)

const

{age=a;}把一个修改类数据成员的函数声明为const

是非法的2021/7/56为了允许修改一个const对象的数据成员,可把相应的数据成员声明为mutable2021/7/57class

Student{public:……int

getAge()const

{ return

age;

}int

setAge(inta)

const

{ age

=a;

}int

getId

()const;…private:char

*name;int

id;mutable

int

age;}统计创建的所有学生对象的个数?学生人数不是任何一个具体学生的属性,具有全局性若用一个全局变量记录学生人数,缺点:容易被误修改,不安全。容易和其它全局变量重名。2021/7/583.8

类的静态成员类相当于一个数据类型,当说明一个某类的对象时,系统就为该对象分配一块内存单元来存放类中的所有成员。但在某些应用中,需要程序中属于某个类的所有对象共享某个数据。为此,一个解决的办法就是将所要共享的数据说明为全局变量,但这将破坏数据的封装性;较好的解决办法是将所要共享的数据说明为类的静态成员。2021/7/59静态数据成员2021/7/510C++中,同一个类定义多个对象时,每个对象拥有各自的数据成员(不包括静态数据成员),而所有对象共享一份成员函数和一份静态数据成员。静态数据成员是类的所有对象中共享的成员,而不是某个对象的成员,因此可以实现多个对象间的数据共享。静态数据成员不属于任何对象,它不因对象的建立而产生,也不因对象的析构而删除,它是类定义的一部分,所以使用静态数据成员不会破坏类的隐蔽性。对静态数据成员的操作和一般数据成员一样,定义为私有的静态数据成员不能由外界访问。静态数据成员可由任意访问权限许可的函数访问。可以在类的成员函数中改变静态数据成员。静态数据成员不从属于任何一个具体对象,所以必须对它初始化,且对它的初始化不能在构造函数中进行。类中用关键字static修饰的数据成员叫做静态数据成员。静态数据成员的定义与一般数据成员相似,但前面要加上static关键词。静态数据成员的初始化与一般数据成员不同,静态数据成员初始化的格式如下:<类型><类名>::<静态数据成员>=<值>;在引用静态数据成员时采用格式:<类名>::<静态数据成员>2021/7/511静态成员函数2021/7/512静态成员函数的定义和其他成员函数一样。但在说明时需注意静态成员函数不得说明为虚函数。静态成员函数与静态数据成员类似,也是从属于类,静态成员函数的定义是在一般函数定义前加上static关键字。调用静态成员函数的格式如下:<类名>::<静态成员函数名>(<参数表>);静态成员函数与静态数据成员一样,与类相联系,不与对象相联系,只要类存在,静态成员函数就可以使用,所以访问静态成员函数时不需要对象。如果用对象去调用静态成员函数,只是用其类型。静态成员函数只能访问静态数据成员、静态成员函数和类以外的函数和数据,不能访问类中的非静态数据成员(因为非静态数据成员只有对象存在时才有意义)。但静态数据成员和静态成员函数可由任意访问权限许可的函数访问。和一般成员函数类似,静态成员函数也有访问限制,私有静态成员函数不能由外界访问。静态成员函数没有this指针,因此,静态成员函数只能直接访问类中的静态成员,若要访问类中的非静态成员时,必须借助对象名或指向对象的指针。2021/7/5132021/7/514静态数据成员被当作类的全局对象,对于非静态数据成员,每个类对象都有自己的拷贝,而静态数据成员对每个类只有一个拷贝,由该类的所有对象共享访问。……student.cpp#include

“student.h”int

Student::count

=

0;Student::Student(){……count++;}Student::~Student(){……count--;}student.hclass

Student{public:Student();~Student();friend

int

getCount();private:char

*name;int

id;mutable

int

age;static

intcount;}intgetCount(){return

Student::count;}class

Student{public:……static

int

getCount()

{

return

count;

}private:char

*name;int

id;mutable

int

age;static

intcount;}2021/7/515int

count

=Student::getCount();在静态成员函数中只能访问静态成员。class

Student{public:……private:char

*name;int

id;mutable

int

age;static

int

count;static

const

int

class

=3306;只有静态整型常量才能在头文件里初始化static

const

char

depart[20]

=

“CSE”;}2021/7/516友元提供了在不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。通过友元,一个普通函数或另一个类中的成员函数可以访问类中的私有成员和保护成员。友元的正确使用能提高程序的运行效率,但破坏了类的封装性和数据的隐蔽性。友元函数定义友元函数的方式是在类定义中用关键词friend说明该函数,其格式如下:friend<类型><友元函数名>(<参数表>);2021/7/5173.9友元友元函数说明的位置可在类的任何部位,既可在

public区,也可在protected区,意义完全一样。友元函数定义则在类的外部,一般与类的成员函数定义放在一起。类的友元函数可以直接访问该类的所有成员,但它不是成员函数,可以像普通函数一样在任何地方调用。友员函数的定义方法是在类的任何地方象定义其他函数一样定义该函数,并在其前面加上关键字friend即可。友员函数虽然在类内定义,但它不是这个类的成员函数,它可以是一个普通函数,也可以是其他类的成员函数,在其函数体中通过对象名访问这个类的私有或保护成员。2021/7/518友元类2021/7/519C++允许说明一个类为另一个类的友元类(friendclass)。如果A是B的友员类,则A中的所有成员函数可以像友员函数一样访问B类中的所有成员。定义格式如下:class

B//A的所有成员函数均为B的友员函数{ friend

class

A;//…}20友元关系不可以被继承。假设类A是类B的友元,而类C从类B派生,如果没有在类C中显式地使用下面的语句:friend

class

A;那么,尽管类A是类B的友元,但这种关系不会被继承到类C,也就是说,类C和类A没有友元关系,类

A的成员函数不可以直接访问类C的受保护成员和私有成员。不存在“友元的友元”这种关系。假设类A是类B的友元,而类B是类C的友元,即是说类B的成员函数可以访问类C的受保护成员和私有成员,而类A的成员函数可以访问类B的受保护成员和私有成员;但是,类A的成员函数不可以直接访问类C的受保护成员和私有202成1/7员/5

,即是说友元关系不存在传递性。2021/7/521求两数的平方差。

#include

<iostream.h>class

Myclass{private:int

a,b,max,min;public:Myclass(int

i,int

j):a(i),b(j){max=(a>b)?a:b;min=(a<b)?a:b;}friend

int

Result(Myclass&x);};int

Result(Myclass&

x){return=x.max*x.max-x.min*x.min;}void

main(){ Myclass

n(3,4);Cout<<Result(n)<<endl;

}此程序的运行结果为:7例:3.10

运算符重载2021/7/522int

a,

b,

c;a=1;b=1;c=1;a=b=c=1;class String

{public:……private:int_size;char

*_string;}String

s1,s2,s3,s4;s1=s2=s3=s4=“hello”;?重载运算符使得类对象可以与运算符一起被使用,使得对类对象的操纵与内置类型的对象一样直观。重载的运算符在类体中被声明,声明方式同普通成员函数一样,只不过它的名字包含关键字operator,以及紧随其后的一个预定义运算符:2021/7/523class

String

{public:……int

_size;

char

*_string;};String&

String::operator=(

const

char

*sobj

){if(

!

sobj

)

{_size

=

0;delete[]

_string;_string

=

0;}else

{_size

=

strlen(

sobj

);delete[]

_string;_string

=

new

char[

_size

+

1

];strcpy(

_string,

sobj

);}return

*this;}……String&

operator=

(

const

char

*

sobj

);String&

operator=(

const

String

&

);private:String

s1,s2,s3;s1=s2=s3=“hello”;2021/7/524class

String

{public:……String&

operator=

(

const

char

*

sobj

);……private:int

_size;

char*_string;};class

String

{……friend

String

&

operator=(String

&

,

const

char

*);……private:2021/7/525int

_size;

char

*_string;};String

&

operator

=

(String

&

str,

const

char

*sobj);另一种实现方式运算符重载的规则:C++中的运算符除了少数几个以外,全部可以重载,而且只能重载已有的这些运算符。重载之后运算符的优先级和结合性都不会改变。运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。一般来讲,重载的功能应当与原有功能相类似,不能改变原运算符的操作对象个数,同时至少要有一个操作对象是自定义类型。2021/7/526注意:对于内置类型的运算符,它的预定义意义不能被改变;//错误:不能为int

重新定义内置的运算符int

operator+(int,

int

);不能为内置数据类型定义其他的运算符;预定义的运算符优先级不能被改变。2021/7/527单目运算符重载2021/7/528类的单目运算符可重载为一个没有参数的非静态成员函数或者带有一个参数的非成员函数,参数必须是用户自定义类型的对象或者是对该对象的引用。在C++中,单目运算符有++和--,它们是变量自动增1和自动减1的运算符。在类中可以对这两个单目运算符进行重载。如同“++”运算符有前缀、后缀两种使用形式,

“++”和“--”重载运算符也有前缀和后缀两种运算符重载形式,以“++”重载运算符为例,其语法格式如下:<函数类型>

operator

++();

//前缀运算<函数类型>operator++(int);//后缀运算29使用前缀运算符的语法格式如下:++<对象>;使用后缀运算符的语法格式如下:<对象>++;使用运算符前缀时,对对象(操作数)进行增量修改,然后再返回该对象。所以前缀运算符操作时,参数与返回的是同一个对象。这与基本数据类型的运算符前缀类似,返回的也是左值。使用运算符后缀时,必须再增量之前返回原有的对象值。为此,需要创建一个临时对象,存放原有的对象,以便对操作数(对象)进行增量修改时,保存最初的值。运算符后缀操作时返回的是原有对象值,不是原有对象,原有对象已经被增量修改,所以,返202回1/7/的5应该是存放原有对象值的临时对象。双目运算符重载2021/7/530对于双目运算符,一个运算数是对象本身的数据,由

this指针给出,另一个运算数则需要通过运算符重载函数的参数表来传递。下面分别介绍这两种情况。对于双目运算符B,如果要重载B为类的成员函数,使之能够实现表达式“oprd1Boprd2”,其中oprd1为A类的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参的类型是oprd2所属的类型。经过重载之后,表达式oprd1

B

oprd2就相当于函数调用

“oprd1.operator

B(oprd2)”。运算符也可以重载为类的友元函数,这样,它就可以自由地访问该类的任何数据成员。这时,运算符所需要的运算数都需要通过函数的形参表来传递,在参数表中形参从左到右的顺序就是运算符运算数的顺序。运算符重载实例2021/7/531->运算符重载赋值运算符重载下标运算符重载运算符new与delete重载逗号运算符重载类型转换运算符重载3.11

类的组合2021/7/532问题:定义一个类,描述一条线段线段具有长度、两个端点等属性线段具有求长度等行为端点可以用另外一个表示点的类表示线段类Line包含两个点类Point的对象类的组合描述的是一个类内嵌其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系。class

Point{public:Point(

int

xx=0,intyy=0){X=xx;Y=yy;}Point(Point

&p);int

GetX()

{

return

X;}int

GetY()

{

return

Y;}private:int

X,Y;};class

Line{public:Line(Point

xp1,Point

xp2);Line(Line&);double

GetLen()

{

return

len;}private:Pointp1,p2;double

len;};2021/7/533当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。因此,在创

建对象时,既要对本类的基本类型数据成员进行初

始化,又要对内嵌对象成员进行初始化。Point::Point(Point

&p){X=p.X;Y=p.Y;}Line::Line(Point

xp1,Point

xp2):p1(xp1),p2(xp2){double

x=(double)(p1.GetX()-p2.GetX());double

y=(double)(p1.GetY()-p2.GetY());len=sqrt(x*x+y*y);}Line::Line(Line

&L)

:p1(L.p1),p2(L.p2){len=L.len;}2021/7/534int

main(){Pointmyp1(1,1),

myp2(4,5);Line

line(myp1,myp2);Line

line2(line);cout<<“The

lengthof

theline

is:”;cout<<line.GetLen()<<endl;cout<<“The

length

of

the

line2

is:”;cout<<line2.GetLen()<<endl;}如何调用Line和2021/7/535Point的构造函数?调用顺序?在创建一个组合类的对象时,不仅它自身的构造函数将被调用,而且还将调用其内嵌对象的构造函数,调用顺序为:1、调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的声明中出现的次序。2、执行本类的构造函数。2021/7/536析构函数的调用顺序与构造函数刚好相反。Point::Point(Point

&p){X=p.X;Y=p.Y;}Line::Line(Point

xp1,Point

xp2):p1(xp1),p2(xp2){double

x=(double)(p1.GetX()-p2.GetX());double

y=(double)(p1.GetY()-p2.GetY());len=sqrt(x*x+y*y);}Line::Line(Line

&L)

:p1(L.p1),p2(L.p2){len=L.len;}int

main(){Point

myp1(1,1),

myp2(4,5);Line

line(myp1,myp2);Line

line2(line);cout<<“The

length

of

the

line

is:”;cout<<line.GetLen()<<endl;cout<<“The

length

of

the

line2

is:”;cout<<line2.GetLen()<<endl;}调用次数和顺序?Point::Point(Point

&)先被调用2次;Line::Line(Point

xp1,Pointxp2)被调用一次Point::Point(Point

&)先被调用2次;Line::Line(Line

&)被调用一次2021/7/537class

A{public:void

f(B,b);};class

B{public:void

g(A,a);}两个类互相引用的情况,称为循环依赖2021/7/5383.12

前向引用声明前向引用声明:在引用未声明的类之前,将该类的名字告诉编译器,使编译器知道这是一个类名。2021/7/539class

B;class

A{public:void

f(B,b);};class

B{public:void

g(A,a);}2021/7/540class

B;class

A{public:void

method(){x->bMethod();}private:B

*x;B

y;};class

B{public:void

bMethod();private:A

*z;}前向引用声明虽然可以解决一些问题,但它不是万能的。尽管使用了前向引用声明,但在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。C++语言异常处理机制的基本思想是将异常的检测与处理分离。当在一个函数体中检测到异常条件存在,但无法确定相应的处理方法时,将引发一个异常,并由函数的直接或间接调用检测并处理这个异常。这一基本思想用3个保留字实现:throw、try和catch。其作用是:try:标识程序中异常语句块的开始。throw:用来创建用户自定义类型的异常错误。catch:标识异常错误处理模块的开始。2021/7/5413.13

异常处理在一般情况下,被调用函数直接检测到异常条件的存在并使用throw引发一个异常(注意,C++语言的异常是由程序员控制引发的,而不是由计算机硬件或程序运行环境控制的);在上层调用函数中使用try检测函数调用是否引发异常,检测到的各种异常由catch捕获并作相应处理。异常处理的语法在C++程序中,任何需要检测异常的语句(包括函数调用)都必须在try语句块中执行,异常必须由紧跟着try语句后面的catch语句来捕获并处理。因而,try与catch总是结合使用。throw、try和catch语句的一般语法如下:2021/7/54243throw<表达式>;try{//try语句块}catch(类型1

参数1){//针对类型1的异常处理}catch

温馨提示

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

评论

0/150

提交评论