类与对象
【学习目标】
本章主要介绍类类型的定义、构造函数与析构函数的定义与作用、友元函数与友元类的声明与作用、派生类的定义与作用、虚函数和多态性的概念、静态数据成员的声明与定义、模板类的定义与实例化等内容。通过本章的学习,要求同学们:
掌握类的概念,类类型的定义格式,类与结构的关系,类与操作符重载,类的成员属性,类的封装性,类的继承性,构造函数和析构函数的作用,this指针的含义,类对象的定义,友元函数与友元类的作用;了解inline成员函数,静态数据成员,类的多态性与虚函数,类模板等内容。
类:是对具有共同属性和行为的一类事物的抽象描述,共同属性被描述为类中的数据成员,共同行为被描述为成员函数。 类所描述的事物具有共同的属性和行为(操作),但每一个具体事物(又称为个体,实例或对象)都具有属于自己的属性值和行为特征。
人的共同属性有姓名,性别,出生日期等等。人的共同行为有爱好,工作,学习等等。
同结构与联合一样,类是一种自定义类型,它包括定义数据成员和定义函数成员(又称成员函数),用数据成员来描述同类事物的属性,用成员函数来描述它们的行为。
类定义变量以及访问数据成员与函数成员与结构类型相同。
一、类的定义
1.类的定义格式:
clsaa类名{成员表};
其中:
类名:为自定义的类的名称,可用来定义变量,函数的数据类型。
成员表:为类包含的数据成员和函数成员。
每一个成员都具有一定的存取权限,或者称存取属性,访问权限,访问属性。C++有三个指明符:
public:公用(公有,公共)访问属性,成员可以任意函数所访问。
private:私有访问属性,成员只能为该类的成员函数所访问。
protected:保护访问属性,成员只能为该类成员函数以及该类的派生类中的成员函数访问。
类中的数据成员一般定义为私有,这样只允许该类的成员函数访问,不允许该类以外的任何函数访问,从而使类对象中的数据得到隐藏和保护。
类与结构在定义时的区别:
结构类型定义时在第一个存取指明符前定义的成员具有默认的public访问属性,类在定义时为private访问属性。
2.定义格式举例:
P245:
1.struct ca
{
int a;
int b;
}ax;
2. class cb
{ int a;
int b;
} bx;
3.class cc
{ int a;
public:
void init(int aa){a=aa;}
int getdata(){return a;}
}cx;
4.class cd
{ char *a;
int b;
public:
void init(char *aa,int bb)
{a=new char[strlen(aa)+1];
strcpy(a,aa);
b=bb;
}
char *geta(){trturn a;}
int getb(){return b;}
void output(){cout<<a<<’ ‘<<b<<endl;}
}dx;
5.class ce
{
private:
int a,b;
int getmax(){return(a>b?a:b);}
public:
int c;
void setvalue(int x1,int x2,int x3)
{a=x1; b=x2; c=x3;}
int getmax()
{int d=getmax():
return (d>c?d:c);
}
}ex,*ep=&ex;
3.类的定义与使用说明
①类中的函数成员与数据成员都是在给定类中定义的。在访问数据成员与函数成员时必须通过对象(变量)调用。
②类的成员函数能够直接访问所在类中定义的所有数据成员与函数成员。
③一个类的每个成员函数中都默认有一个所属类的指针参数(this指针),对数据成员或函数成员的访问,就是对this指针所指对象的访问。
④成员函数可以在类中给出定义,也可以使成员函数的原型与定义分开。
如:P249
⑤成员函数可以通过inline定义的内联函数。
如:P250
⑥成员函数可以说明为可选参数函数。
如:P250
⑦每个类类型的大小,即对应对象所占存储空间的字节数等于所有数据成员所占存储字节数的总和。
⑧类中的成员函数允许重载,包括操作符的重载。
例:P251 例8.1
二、构造函数
构造函数和析构函数属于类中的成员函数。
构造函数与所在的类具有相同的名字,并且不带任何返回类型,也不要返回任何值。
建立构造函数的作用是为对象中的数据成员赋值值。一个类的构造函数将在定义该类对象的时自动调用,完成给对象赋初值的任务,而不允许像其它成员函数那样要由用户直接调用。一般把每个构造函数定义为类的公用成员。
1.无参构造函数和有参构造函数
例如:
例:P255 例8.2
当用类类型定义一个类对象时,若需要它自动调用无参构造函数对其进行初始化,则只需给出对象名,若需要它自动调用有参构造函数对其进行初始化,则给出的对象名后必须带有圆括号括起来的实参数表。
类对象定义所带的参数表中若只有一个参数时,也可以用赋值号代替圆括号。
如:x(5) ---> x=5;
当由动态分配一个类对象时,对该对象进行初始化所需要的参数表应放在new 操作符后的类名的后面,当然调用无参数构造函数时就不带参数表。
如:
array *s1=new array;
array *s2=new array(a,6);
例:P258 例8.3
2.拷贝构造函数
若构造函数的参数是针对同类对象的引用时,则称之为拷贝构造函数或复制构造函数。
通过调用拷贝构造函数,可以定义全个与已有对象完全相同的对象。这个已有的对象是通过 引用参数传递给构造函数的,可称为参数对象。
若一个类中没有定义拷贝构造函数,则系统为该类定义一个默认的拷贝构造函数。
系统默认的拷贝构造函数把参数对象的值赋给初始化的对象中,使得被初始化对象的每个成员值与初始化对象的每个成员值完全相同。
有的类的数据成员中存在着指针,且该指针指向动态分配的存储空间。
例:P260
3.赋值操作符的重载
4.构造函数中的初始化表
在构造函数中对数据成员进行初始化时,可采用两种方法:
①在函数体中使用赋值语句把表达式的值直接赋给成员变量。
②用初始化表进行初始化。
如:P262
当带有初始化表的构造函数执行时,首先执行初始化表,然后才执行函数体。在执行初始化时按照类中数据成员定义的先后次序给数据成员赋初值。
三、析构函数
析构函数的名字与类名相同,只是在函数名前加上(~)以示区别。
析构函数不允许带任何参数,并且也不允许带有返回类型。
通常析构函数是由系统执行程序时自动调用的,但调用时机正好相反,构造函数是在对象生成时调用,析构函数是在对象撤消时调用,且在调用执行后才真正撤消对象。
必须把析构函数定义为公用成员函数。
系统默认定义的析构函数为:
~x(){};
例:P263
四、友元函数和友元类
在一个类中,类对象的私有成员只能由该类的成员函数访问,外部定义的普通函数和其它类中定义的成员函数都不得访问,这些外部函数只能通过该类提供的公用成员函数进行访问,这样有利于数据的封装,隐藏和保护,符合面向程序设计的要求。
为了提高访问效率,C++允许在一个类中把外部的有关函数或类声明(或称为说明,宣布等)为它的友元函数或友元类。
被声明为一个类的友元函数或友元类具有直接访问该类的私有成员的特权。
例:P265例8.4 (友元函数)
P269例8.5 (友元类)
五、类的继承
在C++中允许定义类之间的继承关系,当一个类继承另一个类时,这个类被称为继承类,派生类或子类,另一个类被称为被继承类,基类或父类。
子类能够继承父类的全部特征,包括所有的数据成员和函数成员,并且子类还能够定义父类所没有的,属于自己的特征,即自己定义的数据成员和函数成员。
使用继承,可以避免代码的重写和调试。
例:P272
1.派生类定义的格式:
class派生类名:基类表{成员表};
基类表决定了针对每个基类的继承方式。即在每个基类名前面可以使用的继承权限指明符分别用public,protected和private表示公用继承,保护继承和私有继承。默认为私有继承,即派生类私有继承该基类。
继承方式所影响的是被继承的原成员的访问权限,但无论采用何种继承方式,基类中的私有成员在派生类中是不可访问的。因此继承方式能实际影响的只是原基类中的公用成员和保护成员的访问权限。
类成员受保护程度可分为public,protected和private三个等级,public最低,private最高。
在一个派生类中,其成员由两部分组成,一部分是从基类继承得到的,另一个部分是自己定义的新成员。
每个派生类对象所占的存储空间的大小等于其基类部分的所有数据成员占有的存储空间的大小与新定义部分的所有数据成员占有的存储空间大小的总和。
2.格式举例:
P273
构造函数的执行顺序是,先执行基类构造函数,接着执行类成员所属类的构造函数,最后执行自己定义的构造函数,而析构函数的执行顺序正好相反,它先执行自己的函数体,接着执行类成员所属类的析构函数,最后执行基类的析构函数。
P283 应用举例:
例P283例8.6 ,8. 7