类构成了实现C++面向对象设计的的基础。类是C++封装的基本单元,他把数据和函数封装在一起。当类的成员声明为保护时,外部不能访问,声明为公共时,则在任何地方都可以访问。
1、从结构到类。
C的结构可以把相关联的数据元素组成一个单独的统一体。
说明结构对象的方法是:结构名+变量名
在类中说明的,要么是数据成员,要么是成员函数。他们或者说是public的或者声明为private的。
类具有封装性,它可以说明哪些成员public的哪些不是的。
2、定义成员函数。
在类中定义的函数规模都比较小,语句少。他们一般都为内联函数,即使没有明确用inline标识。
C++中类通常定义在头文件中,因此这些成员函数定义也伴随着进入头文件。我们知道函数声明一般定义在头文件,而函数定义不能在头文件,因为他们将被编译多次。如果是内联函数,包含在头文件中是允许的,因为内联函数在源程序中原地扩展。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
# include <iostream> using namespace std; class tdate { public : tdate(); void set ( int m, int d, int y){ month = m; day = d; year = y; } int isleapyear(){ //判断是否是闰年 return (year & 4 == 0 && year % 100 != 0 ) || (year % 400 == 0 ); } void print(){ cout << month << "/" << day << "/" << year << endl; } ~tdate(); private : int month, day, year; }; tdate::tdate() { } tdate::~tdate() { } void main() { tdate a; a. set ( 8 , 28 , 1996 ); a.print(); system( "pause" ); return ; } |
(2)在类之后定义成员函数。
对于大的成员函数来说,直接把代码放在类的定义中使用起来十分不方便。C++允许在其他地方定义成员函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
# include <iostream> using namespace std; class date { public : date(); void set ( int , int , int ); //成员函数声明 bool is (); void print(); ~date(); private : int month,day,year; }; date::date() { } void date:: set ( int m, int d, int y) { month=m; day=d; year=y; } bool date:: is () { return (year % 4 == 0 && year & 100 != 0 ) || (year % 400 == 0 ); } void date::print() { cout << month << "/" << day << "/" << year << endl; } date::~date() { } void main() { date a; a. set ( 28 , 8 , 1996 ); a.print(); system( "pause" ); return ; } |
将类定义和其成员函数定义分开,是目前开发程序的通常做法。我们把类定义成(头文件)看成是类得外部接口,类的成员函数定义成类的内部实现。将类拿来编写应用程序时,,只需类的外部接口(头文件)。这和我们使用标准库函数的道理是一样的,只需包含某函数声明的头文件,因为类定义中,全部包含成员函数的声明。
在类外部定义成员函数,比在类内部定义时,成员函数名前多加上一个类名。如果该函数的前面没有“类名”::表达式形式把他与该类紧紧连在一起,编译器就会认为该函数是一个普通函数,他与类中的成员函数有相同的名字。以后再连接时,会查出缺少与成员函数相对应而报错。
在类定义内部定义成员函数时,类名神雕了。
类名加在成员函数名之前而不是加在函数的返回值之前。
(3)重载成员函数:
成员函数可以用与传统函数一样的方法重载。但由于类名是成员函数名的一部分,所以一个类的成员函数与另一个类的成员函数即使同名,也不能认为是重载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
# include <iostream> using namespace std; class student { public : student(); float grade(double a){ return a; }; ~student(); private : }; student::student() { } student::~student() { } class slope { public : slope(); float grade(double a){ return a; }; ~slope(); private : }; slope::slope() { } slope::~slope() { } char grade(){ cout << "thank you!" << endl; return 0 ; }; void main() { float a, b; student A; slope B; a = A.grade( 2.1 ); b = B.grade( 3.3 ); cout << a << "-------" << b << endl; grade(); system( "pause" ); } |
在主函数运行时,一共调用了三个grade()函数。
(4)调用成员函数:
一个对象要表现行为,就要调用它的成员函数。调用成员函数的格式类似于访问一个结构对象的分量,先指明对象,在指明分量。他必须指定对象和成员名,否则没有意义。
对象可以通过指针来引导。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
# include <iostream> using namespace std; # include <string> #ifndef STUDENT_H #define STUDENT_H class func { public : func(); int getnum( int a){ number = a; return a; }; void print() { cout << number << endl; return ; } ~func(); private : int number; }; func::func() { } func::~func() { } #endif |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# include <iostream> using namespace std; # include "student.h" func a; void somefunc(func * ps); int main() { somefunc(&a); system( "pause" ); return 0 ; } void somefunc(func * ps) //ps是指向a对象的指针 { ps->getnum( 1 ); if (ps->getnum( 1 )) cout << "good" << endl; else cout << "bad" << endl; return ; } |
通过引用调用成员函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# include <iostream> using namespace std; # include "student.h" func a; void somefunc(func * ps); int main2() { somefunc(&a); system( "pause" ); return 0 ; } void somefunc(func & ps) //ps是指向a对象的指针 { ps.getnum( 1 ); if (ps.getnum( 1 )) cout << "good" << endl; else cout << "bad" << endl; return ; } |
(5)在成员函数中访问成员:
成员函数必须通过对象来调用,在成员函数内部,访问数据成员或成员函数无需如此。
在一个类中所有对象调用的成员函数都是统一代码段。那么成员函数是怎么识别month,day,year ,是属于哪个对象的?
在对象调用s.set(2.5);时,成员函数除了接收3个实参之外,还接受到了一个对象s的地址。这个地址被一个隐含的形参this指针所获取,它等同于执行this=&s,所有对数据成员的访问都隐含的被加上前缀this->。所以:month=m;等价于:this->month=m;等价于 s.month=m;
因此,无论对应哪个对象调用的,成员函数从获得的参数(显式和隐式)来判断。这就是成员函数中访问成员变量无需加上对象名的原因。
(6)保护成员:
类的成员可以声明为被保护的,这时,从类的外部就不能对它进行访问,也可以把成员声明成公共的,公共成员在任何地方都可以被访问。
在类中设置保护屏障,不让外部访问,主要是面向对象程序的目标决定的。
(1)相对于外部函数而言,,保护类的内部数据不被肆意侵犯。(电视机通过一个接口把他提供给外部世界。面板上的按钮就好像是公共成员函数,人人都可以使用,但复杂的电路装载内部,就好像是保护人员,与外部隔离)
(2)使类对它本身内部实现的维护负责。因为只有类自己才能访问该类的保护数据,所以一切对保护数据的维护只有靠类自己了。
(3)限制类与外部世界的接口。把一个类分成两个部分,一部分是公共的,另一部分是私有的。
(4)减少类与其他代码的关联程度。
因该养成类编制的习惯,类定义总是以public,private,开始,让人一目了然。
注意:public和private的区别在类的继承中才表现出来。
(6)屏蔽累得内部实现。
类能够保护它的内部状态。
(7)构造函数主要用于为对象分配内存空间,进行初始化。构造函数的一些特性和析构函数,简析::
1、构造函数的名字必须与类名一样。
2、构造函数可以有任意类型参数,但不能指定返回类型。他有指定的返回值,该值由系统内部指定。
3、狗仔函数时特殊的成员函数,函数体可以写在类的内部,或外部。
4、构造函数可以重载,即一个类中可以定义多个参数个数或参数类型不同的构造函数。
5、构造函数被声明为公有函数,但他不能像其他成员函数一样被显示的调用,它是在定义对象的同时被调用的。
析构函数:也是一种特殊的成员函数,他执行与构造函数相反的操作,通常用于撤销对象使得一些清理任务,如释放分配给对象的内存空间等。析构函数有以下一些特点:
1、析构函数与构造函数名字相同,但他前面必须加上一个~
2、析构函数没有参数,也没有返回值,而且不能被重载,因此一个类中职能有一个析构函数。
3、当撤销对象时,编译系统会自动的调用析构函数。
(8)屏蔽类的内部实现。
1
2
3
4
5
6
7
8
9
|
float Grade(float newpjscore) { if (newpjscore >= 0 && newpjscore <= 5.0 ) { pjscore = newpjscore; } float oldscore = pjscore; return oldscore; } |
这就是访问保护数据成员的途径,student类补充公共成员函数去访问保护数据,在公共成员函数中,可一世就爱种种操作的限制条件,以此对保护数据成员的取值范围加以保护。
编制应用程序,想要使用某个类,所要了解的全部内容就是他的公共成员,他们做什么用!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
# include <iostream> # include <string> # include <math.h> using namespace std; #ifndef POINT_H #define POINT_H class Point { public : Point(); void set (double ix, double iy) { x = ix; y = iy; } double getx() { return x; } double gety() { return y; } double angle() //取点的极坐标 { return (( 180 / 3.1415 ) * atan2(y, x)); } double radius() //取点的极坐标半径 { return sqrt(x*x + y*y); } ~Point(); private : double x; double y; }; Point::Point() { } Point::~Point() { } #endif |
一切对x,y做的操作都由成员函数来完成。
(9)再论程序结构。
1、类得作用域:一个类的所有成员位于这类的作用域内,一个类的任何成员都能访问同一类的任一其他成员。C++认为一个类的全部成员都是一个整体的相关部分。
是指类定义和相应的成员函数的定义范围。
2、可见性:类名允许与其他变量名或函数名相同。当类名与程序中的其他变量或者函数名相同时,可以通过下面的方法正确访问。
(1)如果一个非类型名隐藏了类型名,该类型名通过家前缀即可。
(2)如果一个类型名隐藏了一个非类型名,则用一般作用域规则即可。
(3)在函数中定义的类称为局部类,局部类在面向对象程序设计中并不常见。类作为类型也有作用域,局部类的作用域在定义该类的函数块中。
局部类的成员函数必须在类定义内部定义,因为若在类外部和包含类的函数内部中定义,则导致在函数内部定义的函数的矛盾。
3、类的封装:类的封装的概念首先是,数据与算法(操作)结合,构成一个不可分割的整体(对象)。其次是,在这个整体中一些数据成员是保护的,他们被有效的屏蔽,以防外界的干扰和误操作
。另一些成员是公共的,他们作为接口提供给外界使用。而对该对象的描述即是创建用户定义的类型,对C++来说,就是类的实现机制。
(10)类库的构成:一个商业性C++类库包括一个类的定义和成员函数定义。类定义以头文件的方式提供,成员函数定义则又以一定的计算机硬件或操作系统为背景而编译的代码方式提供。
(11)C++程序结构:
一个c++应用程序,就是一个程序工程。一个C++程序工程文件中,因该组合下面这些程序文件:
1
2
3
|
main.cpp //包含主函数体的程序文件 class .cpp's //用户自定义类库的内部实现程序 function .cpp's //用户自定义的函数库的实现程序 |
小结:
一个类具有数据成员,还具有成员函数,通过成员函数可以对数据成员进行操作,并实现其他功能。
定义一个类之后,可以把该类名作为一种数据类型,定义其“变量”(对象)。
程序利用.操作符,访问类的公共成员。
程序可以在类的外部或内部定义他的成员函数,在类的外部定义成员函数时,必须指出所属的类名,并用全局作用域分辨符(::)把类名和函数名连接起来。
类的成员,包括数据和函数,都可以被说明为公有,保护或私有。公有成员可以在程序中任意访问被访问,而保护或私有成员只能背这个类的成员函数所访问。
把成员说明为保护的,使类的使用之再使用它时,只关心接口,无需关心他的内部实现,即方便了使用,又保护了内部结构。这就是类的封装原理。
含有类的程序结构,充分体现了类的封装和重用,更容易被人所理解。