钱能2 1.docx

上传人:b****8 文档编号:30443222 上传时间:2023-08-15 格式:DOCX 页数:38 大小:94.80KB
下载 相关 举报
钱能2 1.docx_第1页
第1页 / 共38页
钱能2 1.docx_第2页
第2页 / 共38页
钱能2 1.docx_第3页
第3页 / 共38页
钱能2 1.docx_第4页
第4页 / 共38页
钱能2 1.docx_第5页
第5页 / 共38页
点击查看更多>>
下载资源
资源描述

钱能2 1.docx

《钱能2 1.docx》由会员分享,可在线阅读,更多相关《钱能2 1.docx(38页珍藏版)》请在冰豆网上搜索。

钱能2 1.docx

钱能21

面向对象技术(O-O技术)第二讲2009.3.27

例2.构造类成员——类成员初始化

(1)未使用冒号语法

#include

#include

classStudentID

{

public:

StudentID(intid=0)

{

value=id;

cout<<”Assigningstudentid”<

}

~StudentID()

{cout<<”Destructingid”<

protected:

intvalue;

};

classStudent

{

public:

Student(Char*pName=”noName”,intssID=0)

{

cout<<”Constructingstudent”<

strncpy(name,pName,sizeof(name));

name[sizeof(name)-1]=’\0’;

StudentIDid(ssID);//希望将学号传给学号类对象

}

protected:

charname[20];

StudentIDid;

};

voidmain()

{

Students(“Randy”,9818);

}

运行结果为:

Assigningstudentid0

ConstructingstudentRandy

Assigningstudentid9818

Destructingid9818

Destructingid0

(2)使用冒号语法——冒号表示显式调用类的数据成员(类成员)构造函数进行初始化;

1)修改

(1)程序;

2)运行结果:

Assigningstudentid9818

ConstructingstudentRandy

Assigningstudentid0

ConstructingstudentJenny

Destructingid0

Destructingid9818

3)常量和引用的初始化必须放在构造函数的冒号后面;

一般变量的初始化可放在冒号后面,也可放在函数体中;

classSillyClass

{

public:

SillyClass(int&i):

ten(10),refI(i){}

protected:

Constintten;

int&refI;

};

voidmain()

{

inti;

SillyClasssc(i);

}

classSillyClass1

{

public:

SillyClass1()

{

d=10;

}

protected:

intd;

};

classSillyClass2

{

public:

SillyClass2():

d(10){}

protected:

intd;

};

例3.浅拷贝与深拷贝——用一个含堆成员的对象去构造另一个(堆)对象的拷贝构造函数。

(1)浅拷贝:

仅拷贝堆成员指针,并不复制堆成员(资源),仅类成员拷贝,并不拷贝相应的资源。

P1P1

 

拷贝前

P2

 

拷贝后

*运行错误

源程序:

#include

#include

classPerson

{

public:

Person(char*pN)

{

cout<<”Constructing“<

pName=newchar[strlen(pN)+1];

if(pName!

=0)

{strcpy(pName,pN)}

}

~Person()

{

cout<<”Destructing”<

pName[0]=’\0’;

deletepName;

}

protected:

char*pName;

};

voidmain()

{

Personp1(“Randy”);

Personp2=p1;

}

运行结果为:

ConstructingRandy

DestructingRandy

Destructing

Nullpointerassignment

 

(2)深拷贝——不仅拷贝(堆)成员,而且拷贝堆成员(资源),即不仅拷贝类成员,而且拷贝相应的资源。

P1

P1

 

拷贝前

P2

 

拷贝后

源程序:

#include

#include

classPerson

{

public:

Person(char*pN);

Person(Person&p);

~Person();

protected:

char*pName;

};

Person:

:

Person(char*pN)

{

cout<<”Constructing”<

pName=newchar[strlen(pN)+1];

if(pName!

=0)

{

strcpy(pName,pN);

}

}

Person:

:

Person(Person&p)

{

cout<<”Copying“<

pName=newchar[strlen(p.pName)+1];

if(pName!

=0)

strcpy(pName,p.pName);

}

Person:

:

~Person()

{

cout<<”Destructing“<

pName[0]=’\0’;

deletepName;

}

voidmain()

{

Personp1(“Randy”);

Personp2=p1;

}

运行结果为:

ConstructingRandy

CopyingRandyintoitsownblock

DestructingRandy

DestructingRandy

 

§2.3静态成员与友元

一.基本概念

1.静态成员(static)

(1)静态数据成员:

①存放在全局数据区;②默认值为0,一次初始化;③属于类,不属于类实例;④具有公有/私有或保护存储控制说明符;⑤并且在类的成员函数定义中实现(类的内部实现部分中定义为好);⑥访问方式:

类名:

静态成员(见书P335的例子)。

(2)静态成员函数:

①属于类,不属于类实例;②类类型:

静态成员函数名;

③对静态数据成员的访问实现了文件内同类对象之间的通信;④定义位置:

与一般成员函数一样;⑤只认类型,不认对象;⑥静态成员函数没有this指针(与非静态成员函数的根本区别)。

2.友元(friend)

(1)需要友元的原因:

①普通函数直接访问类的保护或私有数据成员,提高访问效率;

②为了方便重载操作符的使用,即方便编程。

(2)说明:

①友元函数不是类成员函数,它是类的朋友,因而能够访问类的全部成员;

②友元声明位置可以是在类的任何部位;

③友元函数定义则在类的外部,一般与类的成员函数定义放在一起;因为类重用时,一般友元是一起提供的。

④友元破坏了类的整体操作性,也破坏了类的封装,使用时权衡利弊。

(3)类型

①普通函数作为类的友元函数;

②一个类的成员函数可以是另一个类的友元;

③整个类是另一个类的友元,该友元称为友类。

3.实例分析

例1(见书p340)

#include

#include

classStudent

{

public:

Student(char*pName);

~Student();

staticStudent*findname(char*pName);//公共静态成员函数

protected:

staticStudent*pFirst;//私有(保护)静态数据成员

Student*pNext;

Charname[40];

};

Student*Student:

:

pFirst=0;//静态成员空间分配及初始化

Student:

Student(char*pName)

{

strncpy(name,pName,sizeof(name));

name[sizeof(name)-1]=’\0’;

pNext=pFirst;

pFirst=this;

}

Student:

~Student()

{

if(pFirst==this)

{

pFirst=pNext;

return;

}

for(Student*pS=pFirst;pS;pS=pS->pNext)

if(pS->pNext==this)

{

pS->pNext==pNext;

return;

}

}

Student*Student:

findname(char*pName)

{

for(Student*pS=pFirst;pS;pS=pS->pNext)

if(strcmp(pS->name,pName)==0)

{returnpS;}

return(Student*)0;

}

voidmain()

{

Students1(“Randy”);

Students2(“Jenny”);

Students3(“Kinsey”);

Student*pS=Student:

findname(“Jenny”);

if(pS)

cout<<”ok.”<

else

cout<<”nofind.”<

}

运行结果为:

ok.

例2友元与运算符重载

运算符重载的形式是:

返回类型operator运算符(参数说明);

1参数和返回类型可以重新说明,即可以重载。

2运算顺序和优先级不能更改。

3参数说明都是内部类型时,不能重载。

4“。

、:

、*、->、?

”五种运算符不能重载,也不能创造新运算符。

实例分析:

(见书p407)

#include

classIncrease

{

public:

Increase(intx):

value(x){}

friendIncrease&operator++(Increase&);//前增量

friendIncreaseoperator++(Increase&,int);//后增量

voiddisplay()

{

cout<<”thevalueis“<

}

private:

intvalue;

};

Increase&operator++(Increase&a)

{

a.value++;//前增量

returna;//再返回原对象

}

Increaseoperator++(Increase&a,int)

{

Increasetemp(a);//通过拷贝构造函数保存原有对象值

a.value++;//原有对象增量修改

returntemp;//返回原有对象值

}

voidmain()

{

Increasen(20);

n.display();

(n++).display();//显示临时对象值

n.display();//显示原有对象

++n;

n.display();

++(++n);

n.display();

(n++)++;//第二次增量操作对临时对象进行

n.display();

}

运行结果为:

thevalueis20

thevalueis20

thevalueis21

thevalueis22

thevalueis24

thevalueis25

调用形式:

对象名.operator++()

对象名.operator++(int)

对象名++/++对象名

讨论:

1.改为类成员形式的定义

1Increase&Increase:

:

operator++();

2IncreaseIncrease:

:

operator++(int);

2.重载赋值运算符与默认赋值运算符

(1)定义格式:

Myclass&operator=(Myclass&s)

(2)默认赋值运算符(重载):

浅拷贝;

(3)与拷贝构造函数的区别

1voidfu(Myclass&mc)

{

MyclassnewMc=mc;//这是拷贝构造函数(newMc对象还不存在时);

newMc=mc;//这是重载赋值运算符(已经存在newMc对象时);

…应用未定义时,使用默认重载赋值运算符;

}

2classmyclass

{

myclassmc1(a,b,c);

myclassmc2=mc1;

myclassmc3(mc2);

}

3.使用方式

(1)运算符使用方式:

Myclasss1(a,b,c);

Myclasss2;

s2=s1;

(2)函数调用方式:

s2.operator=(s1);

实例见书p411—412,不讲了,自己看即可。

#include

#include

className

{

public:

Name()

{

pName=0;

}

Name(char*pn)

{

copyName(pn);

}

Name(Name&s)

{

copyName(s.pName);

}

~Name()

{

deleteName();

}

Name&operator=(Name&s)

{

deleteName();

copyName(s.pName);

return*this;

}

voiddisplay()

{

cout<

}

protected:

voidcopyName(char*pN);

voiddeleteName();

char*pName;

};

voidName:

:

copyName(char*pN)

{

pName=newchar[strlen(pN)+1];

if(pName)

{

strcpy(pName,pN);

}

}

voidName:

:

deleteName()

{

if(pName)

{

deletepName;

pName=0;

}

}

voidmain()

{

Names(“Claudette”);

Namet(“temporary”);

t.display();

t=s;

t.display();//赋值

}

运行结果为:

temporary

Claudette

§2.4类继承与虚函数机制

一.基本概念

1.继承方式

class<类名>:

<访问控制符><基类名>

其中:

访问控制符的语义如下表所示

(1)

基类存取类型

子类继承类型

public

protected

private

public

public/可访

protected/可访

private/不可访

protected

protected/可访

protected/可访

private/不可访

private

private/可访

private/可访

private/不可访

(2)实例见书p389.

classBase

{

public:

intm1;

protected:

intm2;

private:

intm3;

};

classPrivateClass:

privateBase//私有继承

{

public:

voidtest()

{

m1=1;//ok:

将m1据为private

m2=2;//ok:

将m2据为private

m3=3;//不可访问

}

};

classDerivedFromPri:

publicPrivateClass

{

public:

voidtest()

{

m1=1;//不可访问基类的私有成员

m2=2;//不可访问

m3=3;//不可访问

}

};

classProtectedClass:

protectedBase

{

public:

voidtest()

{

m1=1;//m1据为protected

m2=2;//m2据为protected

m3=3;//不可访问

}

};

classDerivedFromPro:

publicProtectedClass

{

public:

voidtest()

{

m1=1;//m1仍为protected

m2=2;//m2仍为protected

m3=3;//不可访问

}

};

classPublicClass:

publicBase//公共继承

{

public:

voidtest()

{

m1=1;//m1为public

m2=2;//m2为protected

m3=3;//不可访问

}

};

classDerivedFromPub:

publicPublicClass

{

public:

voidtest()

{

m1=1;//m1仍保持为public

m2=2;//m2仍保持为protected

m3=3;//不可访问

}

};

voidmain()

{

PrivateClasspriObj;

PriObj.m1=1;//error

PriObj.m2=2;//error

PriObj.m2=2;//error

ProtectedClassproObj;

Probj.m1=1;//error

ProObj.m2=2;//error

ProObj.m3=3;//error

PublicClasspubObj;

PubObj.m1=1;

PubObj.m2=2;//error

PubObj.m3=3;//error

}

(3)protected与private的主要区别

1在单个类中,无区别;

2在继承关系中有区别:

基类的private成员不但对应用程序隐藏,甚至对派生类也隐瞒;

基类的protected成员只对应用程序隐藏,而对派生类则毫不隐瞒。

2.构造函数与析构函数

(1)

单继承:

按派生类次序构造,按构造的相反次序析构;即

(2)多继承:

按派生类次序(从左向右,从上到下)构造,

按构造的相反次序(从右到左,从下到上)析构。

例1:

classb:

publica{}例2:

classd:

publicc,

publicb

{}

 

 

3.多态性:

在运行时,能依据其对象的类型确认哪个函数的能力,称为多态性或迟后联编,实现动态(后期)绑定。

4.面向对象与基于对象的区别:

前者语言支持多态,后者语言支持类而不支持多态(如Ada,VB)。

5.虚函数限制:

①类的成员函数才能说明为虚函数;

②静态成员函数不能是虚函数;

③内联(inline)函数不能是虚函数

④构造函数不能是虚函数;

5析构函数可以是虚函数,而且通常声明为虚函数。

6.类的冗余和类的分解

将共有特征提取出来的过程称为分解(factoring);

分解使类的层次合理化和减少冗余;

(1)银行存款问题:

(储蓄类)(存款类)

*取款操作Withdrawal()也与存储,存款类的取款操作不同;

(2).解决方法

方法

(一):

Checking类作为Savings类的一个派生

讨论:

①结算帐户是储蓄帐户的一个特殊类型,但事实不为如此;

即两类用户合二为一链表:

②银行允许储蓄帐号的余额可以出现允许的范围的负数(一定程度的透支)

·minbalance(透支范围)数据成负;

·并修改withdrawal()虚函数;

③问题checking类也得到了minbalance这个新的数据成员,但对其无用的,增加混淆。

 

方法

(二)为了避免上述方法(-)的问题,另建一个新类(基类)法;并让这两个类都基于新类之上;

二.实例分析(见书p358):

例1.单继承与虚函数

#include

classBase

{

public:

virtualvoidfn()

{cout<<”InBaseClass\n”}

};

classsubclass:

publicBase

{

public:

virtualvoidfn()

{cout<<”InSubclass\n”;}

};

voidtest(Base&b)

{

b.fn();//∴“迟后联编∵fn()Virtual(编译时)“

}

voidmain()

{

Basebc;

Subclasssc;

cout<<”callingtest(bc)\n”;

test(bc);

cout<<”callingtest(sc)\n”;

test(sc);

}

运行结果为:

callingtest(bc)

InBaseclass

Callingtest(sc)

InSubclass

例2继承的访问控制符与派生类数据空间

(1)

classBase

{

public:

intm1;

protected:

intm2;

private:

intm3;

};

classPrivateClass:

privateBase//私有继承

{

public:

voidtest()

{

m1=1;//ok:

将m1据为private

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1