C++【01】类和对象初步认识

一、面向对象程序设计方法概述

1.对象

面向对象程序设计方法概述客观世界中任何一个事物都可以看成一个对象(object)。
任何一个对象都应当具有这两个要素,即属性(attribute)和行为(behavior) 。
在一个系统中的多个对象之间通过一定的渠道相互联系,如图所示。
在这里插入图片描述
面向对象的程序设计采用了以上人们所熟悉的这种思路。使用面向对象的程序设计方法设计一个复杂的软件系统时,首要的问题是确定该系统是由哪些对象组成的,并且设计这些对象。
在C++中,每个对象都是由数据和函数(即操作代码)这两部分组成的,如图所示。
在这里插入图片描述
数据体现了前面提到的“属性” 。函数是用来对数据进行操作的,即对象的行为,在程序设计方法中也称为方法(method)。调用对象中的函数就是向该对象传送一个消息(message),要求该对象实现某一行为(功能)。

2. 封装与信息隐蔽

面向对象程序设计方法的一个重要特点就是“封装性” (encapsulation),所谓“封装”,指两方面的含义:
一是将有关的数据和操作代码封装在一个对象中,形成一个基本单位,各个对象之间相对独立,互不干扰。
二是将对象中某些部分对外隐蔽,即隐蔽其内部细节,只留下少量接口,以便与外界联系,接收外界的消息。这种对外界隐蔽的做法称为信息隐蔽(imformation hiding)。信息隐蔽还有利于数据安全,防止无关的人了解和修改数据。
C++的对象中的函数名就是对象的对外接口,外界可以通过函数名来调用这些函数来实现某些行为(功能)。

3.抽象

在程序设计方法中,常用到抽象(abstraction)这一名词。抽象的过程是将有关事物的共性归纳、集中的过程。
对象是具体存在的,如一个三角形可以作为一个对象,多个三角形可以将它们抽象为一种类型,称为三角形类型。在C++中,这种类型就称为“类(class)”。每个三角形就是属于同一“类”的对象。
类是对象的抽象,而对象则是类的特例,或者说是类的具体表现形式。

4. 继承与重用

如果在软件开发中已经建立了一个名为A的“类”,又想另外建立一个名为B的“类”,而后者与前者内容基本相同,只是在前者的基础上增加一些属性和行为,只需在类A的基础上增加一些新内容即可。这就是面向对象程序设计中的继承机制。利用继承可以简化程序设计的步骤。
“白马”继承了“马”的基本特征,又增加了新的特征(颜色),“马”是父类,或称为基类,“白马”是从“马”派生出来的,称为子类或派生类。
C++提供了继承机制,采用继承的方法可以很方便地利用一个已有的类建立一个新的类。这就是常说的“软件重用”(software reusability) 的思想。

5.多态性

如果有几个相似而不完全相同的对象,有时人们要求在向它们发出同一个消息时,它们的反应各不相同,分别执行不同的操作,这种情况就是多态现象。如,在Windows环境下,用鼠标双击一个文件对象(这就是向对象传送一个消息),如果对象是一个可执行文件,则会执行此程序,如果对象是一个文本文件,则启动文本编辑器并打开该文件。
在C++中,所谓多态性(polymorphism)是指: 由继承而产生的相关的不同的类,其对象对同一消息会作出不同的响应。多态性是面向对象程序设计的一个重要特征,能增加程序的灵活性。

面向对象程序设计的特点

传统的面向过程程序设计是围绕功能进行的,用一个函数实现一个功能。所有的数据都是公用的,一个函数可以使用任何一组数据,而一组数据又能被多个函数所使用(如图所示)。
面向对象程序设计采取的是另外一种思路。它面对的是一个个对象。实际上,每一组数据都是有特定的用途的,是某种操作的对象。也就是说,一组操作调用一组数据。
在这里插入图片描述

5.类和对象的作用

类是C++中十分重要的概念,它是实现面向对象程序设计的基础。
类是所有面向对象的语言的共同特征,所有面向对象的语言都提供了这种类型。一个有一定规模的C++程序是由许多类所构成的。
C++支持面向过程的程序设计,也支持基于对象的程序设计,又支持面向对象的程序设计。
基于对象就是基于类。与面向过程的程序不同,基于对象的程序是以类和对象为基础的,程序的操作是围绕对象进行的。在此基础上利用了继承机制和多态性,就成为面向对象的程序设计。
在面向过程的结构化程序设计中,人们常使用这样的公式来表述程序:
程序=算法+数据结构
算法和数据结构两者是互相独立、分开设计的,
面向过程的程序设计是以算法为主体的。

二、类的声明和对象的定义

1.类和对象的关系

在C++中对象的类型称为类(class)。
类是对象的抽象,而对象是类的具体实例(instance)。
在C++中也是先声明一个类类型,然后用它去定义若干个同类型的对象。对象就是类类型的一个变量。
类是抽象的,不占用内存,而对象是具体的,占用存储空间。

2.声明类型

在C++中声明一个类类型和声明一个结构体类型是相似的。
下面是声明一个结构体类型的方法:

struct Student       //声明了一个名为Student的结构体类型
{ int num;
  char name[20];
  char sex;
};
Student  stud1,stud2; //定义了两个结构体变量stud1和stud2


声明一个类:

class Student                         //以class开头
{ int num;
  char name[20];     
  char sex;                        //以上3行是数据成员    
  void display( )                   //这是成员函数
	{cout<<″num:″<<num<<endl;
	  cout<<″name:″<<name<<endl; 
	  cout<<″sex:″<<sex<<endl;        //以上4行是函数中的操作语句
	} 
};
Student  stud1,stud2;            //定义了两个Student 类的对象stud1和stud2 

可以看到,类(class)就是对象的类型。实际上,类是一种广义的数据类型。类这种数据类型中既包含数据,也包含操作数据的函数。
不能把类中的全部成员与外界隔离,一般是把数据隐蔽起来,而把成员函数作为对外界的接口。
可以将上面类的声明改为

class Student                        //声明类类型 
{ private:                           //声明以下部分为私有的
	      int num;
	      char name[20];     
	      char sex;                        
  public:                            //声明以下部分为公用的
       void display( )                
          {cout<<″num:″<<num<<endl;
            cout<<″name:″<<name<<endl;
            cout<<″sex:″<<sex<<endl; }      
 };
Student  stud1,stud2;                  //定义了两个Student 类的对象

在声明类类型时,声明为private的成员和声明为public的成员的次序任意,既可以先出现private部分,也可以先出现public部分。
如果在类体中既不写关键字private,又不写public,就默认为private。
在一个类体中,关键字private和public可以分别出现多次。每个部分的有效范围到出现另一个访问限定符或类体结束时(最后一个右花括号)为止。
但是为了使程序清晰,应该养成这样的习惯: 使每一种成员访问限定符在类定义体中只出现一次。
在以前的C++程序中,常先出现private部分,后出现public部分,如上面所示。现在的C++程序多数先写public部分,把private部分放在类体的后部。这样可以使用户将注意力集中在能被外界调用的成员上,使阅读者的思路更清晰一些。
在C++程序中,经常可以看到类。为了用户方便,常用的C++编译系统往往向用户提供类库(但不属于C++语言的组成部分),内装常用的基本的类,供用户使用。
在声明类类型的同时定义对象

class Student                           //声明类类型
{ public:                            //先声明公用部分
       void display( )                
       {cout<<″num:″<<num<<endl;
         cout<<″name:″<<name<<endl;
         cout<<″sex:″<<sex<<endl; }
  private:                           //后声明私有部分
      int num;
      char name[20];     
      char sex;                        
 }stud1,stud2;                  //定义了两个Student类的对象

不出现类名,直接定义对象

class                                //无类名
{private:                           //声明以下部分为私有的
┆                        
 public:                            //声明以下部分为公用的
┆                        
}stud1,stud2;                      //定义了两个无类名的类对象


直接定义对象,在C++中是合法的、允许的,但却很少用,也不提倡用。在实际的程序开发中,一般都采用上面3种方法中的第1种方法。
在定义一个对象时,编译系统会为这个对象分配存储空间,以存放对象中的成员。
C++增加了class类型后,仍保留了结构体类型(struct),而且把它的功能也扩展了。如可以将前面用关键字class声明的类类型改为用关键字struct:

struct Student                          //用关键字struct来声明一个类类型
{private:                              //声明以下部分为私有的
      int num;                             //以下3行为数据成员
      char name[20];     
      char sex;                        
 public:                              //声明以下部分为公用的
      void display( )                     //成员函数
     {cout<<″num:″<<num<<endl;
       cout<<″name:″<<name<<endl;
       cout<<″sex:″<<sex<<endl; }      
};
Student  stud1,stud2;                  //定义了两个Student类的对象

3.类的成员函数

类的成员函数(简称类函数)是函数的一种,它的用法和作用和一般函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是: 它是属于一个类的成员。它可以被指定为private(私有的)、public(公用的)或protected(受保护的)。
在使用类函数时,要注意调用它的权限(它能否被调用)以及它的作用域(函数能使用什么范围中的数据和函数)。例如私有的成员函数只能被本类中的其他成员函数所调用,而不能被类外调用。
成员函数可以访问本类中任何成员(包括私有的和公用的),可以引用在本作用域中有效的数据。
一般的做法是将需要被外界调用的成员函数指定为public,它们是类的对外接口。
类的成员函数是类体中十分重要的部分。如果一个类中不包含成员函数,就等同于C语言中的结构体了,体现不出类在面向对象程序设计中的作用。
在类外定义成员函数:

class Student                       
{ public:
       void display( );                  //公用成员函数原型声明
  private:
       int num;
       string name;     
       char sex;                        //以上3行是私有数据成员    
};
void Student∷display( )                 //在类外定义display类函数
     {cout<<″num:″<<num<<endl;          //函数体
       cout<<″name:″<<name<<endl; 
       cout<<″sex:″<<sex<<endl;   
     }
Student stud1,stud2;                      //定义两个类对象

注意: 在类体中直接定义函数时,不需要在函数名前面加上类名,因为函数属于哪一个类是不言而喻的。但成员函数在类外定义时,必须在函数名前面加上类名,予以限定(qualifed),“∷”是作用域限定符(field qualifier)或称作用域运算符,用它声明函数是属于哪个类的。
如果在作用域运算符“∷”的前面没有类名,或者函数名前面既无类名又无作用域运算符“∷”,如
∷display( ) 或 display( )
则表示display函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。
类函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应在函数定义之前,否则编译时会出错。
在类的内部对成员函数作声明,而在类体外定义成员函数,这是程序设计的一种良好习惯。如果一个函数,其函数体只有2~3行,一般可在声明类时在类体中定义。多于3行的函数,一般在类体内声明,在类外定义。
这样不仅可以减少类体的长度,使类体清晰,也有助于把类的接口和类的实现细节相分离。从类的定义中用户只看到函数的原型,而看不到函数执行的细节,从而提高软件工程的质量。

4.内置成员函数

class Student                       
{ public:
       inline void display( );                  //声明此成员函数为内置函数
private:
    int num;
    string name;     
    char sex;                          
};
inline void Student∷display( )          // 在类外定义display函数为内置函数
   {cout<<″num:″<<num<<endl;          
     cout<<″name:″<<name<<endl; 
     cout<<″sex:″<<sex<<endl;   
    }

5.成员函数的存储方式

用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配存储单元,如图2.4所示。
在这里插入图片描述
能否只用一段空间来存放这个共同的函数代码段,在调用各对象的函数时,都去调用这个公用的函数代码。
在这里插入图片描述
显然,这样做会大大节约存储空间。C++编译系统正是这样做的,因此每个对象所占用的存储空间只是该对象的数据部分所占用的存储空间,而不包括函数代码所占用的存储空间。
示例:

class Time
   {public:
         int hour;
         int minute;
         int sec;
         void set( )
             {cin>>a>>b>>c;}
   };
cout<<sizeof(Time)<<endl;

输出的值是12。
这就证明了一个对象所占的空间大小只取决于该对象中数据成员所占的空间,而与成员函数无关。

三、对象成员的引用

在程序中经常需要访问对象中的成员。访问对象中的成员可以有3种方法:
通过对象名和成员运算符访问对象中的成员;
通过指向对象的指针访问对象中的成员;
通过对象的引用访问对象中的成员。

1.通过对象名和成员运算符访问对象中的成员

例如在程序中可以写出以下语句:
stud1.num=1001; //假设num已定义为公用的整型数据成员
表示将整数1001赋给对象stud1中的数据成员num。其中“.”是成员运算符,用来对成员进行限定,指明所访问的是哪一个对象中的成员。
访问对象中成员的一般形式为
对象名.成员名
不仅可以在类外引用对象的公用数据成员,而且还可以调用对象的公用成员函数,但同样必须指出对象名,如
stud1.display( ); //正确,调用对象stud1的公用成员函数
display( ); //错误,没有指明是哪一个对象的display函数
由于没有指明对象名,编译时把display作为普通函数处理。
应该注意所访问的成员是公用的(public)还是私有的(private)。在类外只能访问public成员,而不能访问private成员,如果已定义num为私有数据成员,下面的语句是错误的:
stud1.num=10101; //num是私有数据成员,不能被外界引用

2.通过指向对象的指针访问对象中的成员

class Time
   {public:                //数据成员是公用的
         int hour;
         int minute;
   };
Time  t,*p;                //定义对象t和指针变量p
p=&t;                     //使p指向对象t
cout<<p->hour;            //输出p指向的对象中的成员hour
p->hour表示p当前指向的对象t中的成员hour,在p指向t的前提下,p->hour,(*p).hour和t.hour三者等价。

3.通过对象的引用来访问对象中的成员

Time t1;                //定义对象t1
Time &t2=t1;            //定义Time类引用变量t2,并使之初始化为t1
cout<<t2.hour;          //输出对象t1中的成员hour

四、类的封装性和信息隐蔽

1.公有接口和私有接口的分离

在声明了一个类以后,用户主要是通过调用公用的成员函数来实现类提供的功能。因此,公用成员函数是用户使用类的公用接口(public interface),或者说是类的对外接口。
通过成员函数对数据成员进行操作称为类的实现,为防止用户修改,往往不让用户看到函数的源代码,也就是说实现的细节对用户是隐蔽的,这种实现称为私有实现(private implementation)。
“类的公用接口与私有实现的分离”形成了信息隐蔽。
软件工程的一个最基本的原则就是将接口与实现分离,信息隐蔽是软件工程中一个非常重要的概念。它的好处在于:
(1) 如果想修改或扩充类的功能,只需修改本类中有关的数据成员和与它有关的成员函数,程序中类外的部分可以不必修改。
(2) 如果在编译时发现类中的数据读写有错,不必检查整个程序,只需检查本类中访问这些数据的少数成员函数。

2.类声明和成员函数定义的分离

在面向对象的程序开发中,一般做法是将类的声明(其中包含成员函数的声明)放在指定的头文件中,用户如果想用该类,只要把有关的头文件包含进来即可。
为了实现信息隐蔽,对类成员函数的定义一般不放在头文件中,而另外放在一个文件中。

//student.h                 (这是头文件,在此文件中进行类的声明)
class Student                            //类声明        
{ public:
       void display( );                  //公用成员函数原型声明
  private:
       int num; 
       char name[20];     
       char sex;                            
};
//student.cpp                            //在此文件中进行函数的定义
#include <iostream>
#include ″student.h″                     //不要漏写此行,否则编译通不过
void Student∷display( )                 //在类外定义display类函数
{cout<<″num:″<<num<<endl;          
 cout<<″name:″<<name<<endl; 
 cout<<″sex:″<<sex<<endl;   
}

3.面向对象程序设计中的几个名词

类的成员函数在面向对象程序理论中被称为“方法”(method),“方法”是指对数据的操作。
外界是通过发“消息”来激活有关方法的。所谓“消息”,其实就是一个命令,由程序语句来实现。前面的stud.display( );就是向对象stud发出的一个“消息”,通知它执行其中的display“方法”(即display函数)。
上面这个语句涉及3个术语: 对象、方法和消息。stud是对象,display( )是方法,语句“stud.display( );”是消息。
列:

#include <iostream>
using namespace std;
class Time                     //定义Time类
{public:                      //数据成员为公用的
      int hour;
      int minute;
      int sec;
};

int main( )
{ Time t1;                   //定义t1为Time类对象
   cin>>t1.hour;              //输入设定的时间
   cin>>t1.minute;
   cin>>t1.sec;
   cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;    //输出时间
   return 0;
}

注意:
(1) 在引用数据成员hour,minute,sec时不要忘记在前面指定对象名。
(2) 不要错写为类名,如写成Time.hour,Time.minute,Time.sec是不对的。因为类是一种抽象的数据类型,并不是一个实体,也不占存储空间,而对象是实际存在的实体,是占存储空间的,其数据成员是有值的,可以被引用的。
(3) 如果删去主函数的3个输入语句,即不向这些数据成员赋值,则它们的值是不可预知的。

猜你喜欢

转载自blog.csdn.net/The_Handsome_Sir/article/details/106738086