C++基础(二):类和对象
一、类和对象的作用
类是实现面向对象程序设计的基础。基于对象就是基于类,基于对象的程序是以类和对象为基础的,程序的操作是围绕对象进行的。
在基于过程的程序中,数据是公用的,并且缺乏保护的,数据的交叉使用很容易程序出现错误。程序中的每一组数据都是有特定用途的,是为了某种操作而准备的,也就是说,一组数据与一组操作相对应,因此人们把相关的数据和操作放在一起形成一个整体,与外界相对分隔开。这就是面向对象的程序设计中的对象。
基于过程的结构化程序设计中:程序=算法+数据结构
面向对象的程序设计中:对象=算法+数据结构。程序=对象s+消息(对象s表示多个对象)
消息的作用就是对对象的控制。程序设计的关键是设计好每一个对象以及确定向这些对象发出的命令,使各对象完成相应的操作。
二、类的声明和对象的定义
类是对象的抽象,对象是类的具体实例。
1、声明类类型
类是用户建立的类型。程序中用到类类型,必须自己根据需要进行声明,或者用别人设计好的类。
在C++中声明一个类类型:
class Student //以class开头,类名Student
{
int num;
char name[20];
char sex;
void display() //成员函数
{
cout<<"num:"<<num<<end;
cout<<"name:"<<name<<end;
cout<<"sex:"<<sex<<end;
}
};
Student stud1,stud2; //定义了两个student类的对象stud1,stud2
如果在类的定义中不指定是私有的(private)还是公有的(public),则系统默认为私有的,上面就属于此类情况,外界无法调用,封装在类对象stud1和stud2外界都无法调用,这样的类是毫无用处的。改进代码如下:
class Student //以class开头,类名Student
{ private:
int num;
char name[20];
char sex;
public:
void display() //成员函数
{
cout<<"num:"<<num<<end;
cout<<"name:"<<name<<end;
cout<<"sex:"<<sex<<end;
}
};
Student stud1,stud2; //定义了两个student类的对象stud1,stud2
现在声明了display函数是公用的,外界可以调用该函数了。被声明为私有类(private)成员,只能被本类中的成员引用,类外不能调用。被声明为公用的(public)成员,既可以被本类中的成员函数所引用,也可以被类的作用域内的其他函数引用。
除此之外,还有一种成员访问限定符projected(受保护的),被声明为受保护的成员,不能被类外访问,但可以被派生类的成员函数访问。
归纳为一般形式:
class [类名]
{
private:
[私有的类和成员函数];
public:
[公有的类和成员函数];
};
在一个类体中,关键字private和public可以分别出现多次,每个部分有效范围到出现另一个访问限定符或类体结束为止。为了使程序清晰,应该养成使每个成员访问限定符在类定义体只出现一次的习惯
2、定义对象
方法一:先声明类型,然后在定义对象
Student stud1,stud2; //Student是已经声明的类类型
方法二:在声明类的同时定义对象
class Student //以class开头,类名Student
{ private:
int num;
char name[20];
char sex;
public:
void display() //成员函数
{
cout<<"num:"<<num<<end;
cout<<"name:"<<name<<end;
cout<<"sex:"<<sex<<end;
}
}stud1,stud2; //定义了两个student类的对象stud1,stud2
方法三:不出现类名直接定义对象
class
{ private:
int num;
char name[20];
char sex;
public:
void display() //成员函数
{
cout<<"num:"<<num<<end;
cout<<"name:"<<name<<end;
cout<<"sex:"<<sex<<end;
}
}stud1,stud2;
程序开发中建议只使用方法一。
三、类的成员函数
1、成员函数性质
类函数,类的成员函数,和一般函数一样,也有返回值和函数类型,区别是:它是属于一个类的成员,出现在类体中,它可以被指定为private(私有的)、public(公有的)或protect(受保护的)。
类的成员函数是类体中十分重要的部分,如果一个类中不包含成员函数,就相当于C语言中的结构体,丧失了面向对象程序中的优势。
2、在类外定义成员函数
可以在类体中定义成员函数,也可以在类外定义。
class Student
{
public:
void display(); //公用函数原型声明
private:
int num;
string name;
char sex;
};
void Student::display()
{
cout<<"num:"<<num<<end;
cout<<"name:"<<name<<end;
cout<<"sex:"<<sex<<end;
}
Student stud1,stud2;
"::"是作用域限定符,用它声明函数是哪一类的。类函数必须先在类体中做原型声明,然后在类外定义,所以类体的函数必须在函数定义之前,否则编译会出错。
3、内置成员函数
C++只有在类外定义的成员函数规模很小而调用频率较高时,才指定内置函数。在类体中定义的成员函数中不包括循环等控制结构,C++系统自动对他们作为内置函数(inline)处理。
4、成员函数的储存方式
用类去定义对象时,系统会为每一个对象分配存储空间。**一个对象占用的空间大小取决于该对象中数据成员所占用的空间,而与成员函数无关。**函数的目标代码存储于对象空间之外。(1)不管成员函数是在类外定义还是类类定义(2)不管成员函数是否用inline声明,这两者情况下都不占用对象的存储空间(inline函数只影响程序的执行效率,而与成员函数是否占用对象的存储空间无关)。
三、对象成员的引用
访问对象的成员有三种方法:
- 通过对象名和成员运算符访问成员中的对象。
- 通过指向对象的指针访问对象中的成员。
- 通过对象的引用访问对象中的成员。
1、通过对象名和成员运算符访问成员中的对象
访问对象成员的一般形式:[对象名].[成员名]
stud.num = 1002; //正确。如果num是私有数据成员,则无法被引用
num = 1002; //错误
表示将整数1002赋值给对象stud1中的数据成员num,“.”是成员运算符。如果只写成员名而忽略对象名,则意味着将1002赋值给了普通的整型变量num。
不仅可以在类外引用对象的公用数据成员,而且还可以调用对象的公用成员函数,但同样必须指出对象名。如,
stud1.display(); //正确,调用stud1的公用成员函数
display(); //错误,没有指明哪一个对象的display函数
此外,还需要注意所访问的成员是公用的(public)还是私有的(private)。在类外只能访问public成员,而不能访问private成员。同样的,在类外只能调用public成员函数,因此在一个类中至少有一个公用的成员函数,作为对外的接口,否则就无法对对象进行任何操作。
2、通过指向对象的指针访问对象中的成员
c语言中可以通过指针去引用结构体中的成员,指针访问对象中的成员的方法与此类似。
class Time
{
public:
int hour;
int minute;
};
Time t,*p;
p = &t;
cout<<p->hour;
在p指向t的前提下,p->t,(*p).hour和t.hour三者等价。
四、类的封装性和信息隐藏
1、公用接口与私有实现的分离
C++通过类来实现封装性,把数据和这些数据有关的操作封装在类中。在类声明中,一般把所有的数据指定为私有的,使他们与外界隔离;把需要让外界调用的公用成员函数指定为公用。在类外虽然不能直接访问私有数据成员,但可以通过调用公用成员函数来引用甚至修改私有数据成员。
外界只能通过公用成员函数来实现对类中的私有数据的操作,因此,公用成员函数是用户使用类的公用接口(public interface),或者说是类的对外接口。
在声明了一个类以后,用户只需要通过调用公用成员函数来实现功能,并不需要了解内部功能实现的细节,这就是接口与实现的分离。
可以看到:类中被操作的数据是私有的,类的功能的实现细节对用户是隐藏的,这种实现成为私有实现。这种类的公用接口与私有实现的分离形成了信息隐蔽。
将接口与实现的分离这种原则的好处是:
(1)如果想修改或扩充类的功能,只需要修改类中的有关数据成员和它有关的成员函数,程序中类以外的部分不需要修改。例如在原有的类中增加数据成员的年龄:
class Student{
private:
int num;
string name;
int age;
char sex;
public:
void display()
{
cout<<"num:"<<num<<end;
cout<<"name:"<<name<<end;
cout<<"age:"<<sex<<end;
cout<<"sex:"<<sex<<end;
}
};
Student stud;
注意:虽然类中的数据成员改变了,成员函数display的定义改变了,但是类的对外接口没有改变,外界任然通过公用的display函数访问类中的数据。
不难看出,当接口与实现(对数据的操作)分离时只要类的接口没有改变,对私有实现的修改不会引起程序其他部分的修改。
(2)如果在编译时发现类中的数据读写有错误,不必检查整个程序,只需要检查本类中访问这些数据的少数成员函数。
2、类声明和成员函数的分离
在面向对象程序开发中,常常把有关的类声明放到指定的头文件中,用户想使用此类,只需把有关的头文件包含进来即可,因此可以认为类声明的头文件是用户使用类库的公用接口。
包含成员函数定义的文件就是类的实现。类声明和函数定义是分别放在两个文件中的。
一个C++程序通常包括:
- 类声明头文件(后缀为.h)
- 类实现文件(后缀.cpp),它包括类成员函数定义
- 类的使用文件(后缀为.cpp),即主文件
在程序中使用#include指令包含类声明头文件,在编译时把类的使用文件和类实现文件按多文件程序的规定组成一个统一的程序,经过编译连接后运行。不必每次都对类实现文件(类成员函数定义文件)进行编译,只需编译一次即可,把第一次编译后形成的目标文件保存起来,以后需要时直接与程序的目标文件链接即可。
实际工作中,通常将若干个常用的功能相近的类声明集中在一起,形成各种类库。
类库包括两部分:(1)包括类声明的头文件,(2)经过编译的成员函数的定义,它是目标文件。用户只需将类库装入自己的计算机系统,并在程序中用#include指令将有关的类声明的头文件包含到程序中,就可以使用这些类和其中的成员函数。