钱能2 1.docx
《钱能2 1.docx》由会员分享,可在线阅读,更多相关《钱能2 1.docx(38页珍藏版)》请在冰豆网上搜索。
![钱能2 1.docx](https://file1.bdocx.com/fileroot1/2023-8/15/e0a5b781-ad6d-47f8-b3f0-8dc155b9eee4/e0a5b781-ad6d-47f8-b3f0-8dc155b9eee41.gif)
钱能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