类的概念及其使用
总结以下有关类的概念,用于加深对类的理解。可能有不足或错误之处,欢迎指出,大家共同进步。
1、引言
介绍类,绕不过去两个概念:面向过程编程、面向对象编程(Object Oriented Programming)。其中面向过程的编程以C语言为代表的编程方式,面向过程程序的设计方式是:确定程序需要哪些过程,然后采用最合适的算法来实现;强调的是程序实现的过程以及实现过程中采用的算法。这程序开发的前期,采用这种方式是最直接也是最简单的,因为当时面对的程序规模还比较小,采用面向对象的设计方式能够满足基本的需要。随着程序的规模越来越大,采用面向对象的方式已不能很好满足编程的需要,此时,提出了OOP的概念。OOP与强调过程的编程方式不同,它强调的是对象,把程序中的组成分解成一个个对象,然后通过对象之间的操作或交互来满足程序的需要。
与面向过程相比,OOP具备以下几个重要的特性:
1)、抽象
2)、封装和隐藏
3)、多态
4)、继承
5)、代码重用性
为了实现这些特性并把它们组合在一起,提出了类的概念。
2、类的概念
何为类?类的英文表达为class,直译过来的意思就是“类型”,即,一个类是自定义的一种类型。
这就不得不提及OOP中“抽象”的特性。世界是无序和复杂的,简单来说,一个人的组成就非常复杂,到目前为止,在医学上还在不断地探索人体的奥秘。而在OOP中,如果要表达一个人,我们不可能把所有与人体相关的组成或行为都表现出来,那样会非常复杂和繁琐,所以我们就需要化繁为简,找出人具备的明显的特征组合起来来表达“人”,这种化繁为简的过程就可以称之为抽象。通过抽象的分析,一个“人”可能包括:具有脑袋、五官、四肢、肤色等组成属性,具备直立行走、使用工具等行为。经过抽象,可以把“人”分析为一组具有多种属性、多种行为的描述,而在OOP中,类就是用来组合抽象出的属性和行为,从而形成一个用来描述“人”的一种类型。这种组合的过程其实可以理解为对类的封装的隐藏,通过封装后的类可以有效地用来表达一个“人”这种实体。
3、对象的概念
那么何为对象呢?我们现在已经知道类是一种类型,而对象就是该类型实例化出来的一个实体,该实体可以成为类的对象。比如“人”是一个类,那么我们每一个人个体都可以称之为一个个对象,一个类型为“人”的对象,我们都具备该类描述的属性和行为。对象是类的一种实例化,在OOP中,当定义好类之后,操作的主要就是对象。
4、类的继承
继承是类的一个很重要的概念,是实现OOP特性中代码重用性的一种手段。正如其字面意思,通过继承,不仅可以使用原始类中一些属性和行为,同时也可以对继承的类进行扩展,形成新的属性和行为。
继承,又可以称之为派生,当从一个类派生出另外一个类时,原始类称之为基类(超类),继承类则称之为派生类。集成的方式有三种:Public、Protected、Private,分别对应类中的三种类型的属性。继承的形式如下所示:
class CChildClass : public CBaseClass
{...}
其中CChildClass属于派生类,CBaseClass属于基类,继承方式为公有继承,即是是公有继承也不能直接调用基类的私有成员,只能通过基类的公有方法进行调用。关于继承有以下需要注意的几点:
1)、构造函数和析构函数。需要注意的是,当创建派生类对象是,程序首先创建基类的对象。这也就是说,首先调用的是基类的构造函数,然后再调用派生类的构造函数。而析构函数的调用顺序刚好相反,是先调用派生类的析构函数然后再调用基类的析构函数;
2)、构造函数的初始化列表。当采用继承的方式时,仍然可以通过初始化列表的形式初始化类的属性,具体使用方式如下所示:
CChildClass: :CChildClass(int p1, char p2, string p3): CBaseClass(p2, p3)
{attr1 = p1; }
当定义一个CChildClass类的对象时如:CChildClass *cc = new CChildClass(1, 'a', "Hello");就会按照如上方式进行属性的初始化,先将p2和p3传递给基类的构造函数进行初始化操作,然后在执行派生类的构造函数进行属性的初始化。
当然,还有另外一种形式的初始化列表,即所有的属性都通过初始化列表的形式进行初始化,如下所示:
CChildClass: :CChildClass(int p1, char p2, string p3): CBaseClass(p2, p3),attr1(p1)
{...}
3)、思考一种情况,派生类同时也继承了基类的行为(即函数),那么如果,派生类的行为与基类不一样,或者说每一个该基类的派生类的行为都不一样,那该怎么办呢?
比如:现在有一个“车”的类,类名为CCar,它有一个名为Drive的行为,即:
class CCar
{...
public:
void Drive();
}
现有两个子类,分别为CManualCar和CAutoCar,分别代表手动挡驾驶,和自动档驾驶的车,那么他们的继承方式就为如下所示:
class CManualCar : public CCar
{...
}
class CAutoCar : public CCar
{...
}
CManualCar和CAutoCar分别继承了基类的Drive行为,但是他们的Drive行为并不相同,那么该如何解决呢?
当然可以采用在每一个派生类中再定义一个行为分别代表手动驾驶和自动驾驶,但是这样做继承的意义就不大,而且不符合代码重用的特性。这是就可以引用类的另外一个重要的概念了——多态。
5、类的多态
多态是类的一种很重要的特性。从字面意思上就可以理解,类的多态性,及类可以有多种形态。那么实现多态的手段是什么呢?虚函数。通过virtual来声明虚函数,如下所示:
class CCar
{...
public:
virtual void Drive() const; //const 关键字的作用是声明该函数为“只读”函数,不会修改类中数据成员的值
}
此时派生的两个子类就可以采用如下形式:
class CManualCar : public CCar
{...
public:
virtual void Drive() const;
}
class CAutoCar : public CCar
{...
public:
virtual void Drive() const;
}
调用方式如下所示:
CCar car; CManualCar manualCar; CAutoCar autoCar;
car.Drive(); //调用的是CCar .Drive()
manualCar.Drive(); //调用的是CManualCar .Drive()
autoCar.Drive(); //调用的是CAutoCar .Drive()
关于虚函数的使用有以下需要注意的地方:
1)、虚析构函数。析构函数的调用事发生当delete释放由new创建的对象时,对于派生类来说,当delete时,如果基类中的析构函数没有虚化,则将只会调用基类中的析构函数。而如果基类的析构函数虚化了,则将会先调用派生类的析构函数,然后再调用基类的析构函数。虚化基类的析构函数就是为了保证析构的顺序是正确的。
2)、纯虚函数。纯虚函数是更加高级一点的虚函数。虚函数在基类中也是要有实现的,而纯虚函数在基类中是不需要实现的,因为纯虚函数实际上是提供了一种借口,所有的派生类都可以从接口中继承对应的函数,对应有很多共性的对象而言,采用纯虚函数还是比较合适的。纯虚函数的声明方式如下所示:
class CCar
{...
public:
virtual void Drive() const=0;
}
class CManualCar : public CCar
{...
public:
virtual void Drive() const; //在派生类中则不需要加 =0
}
需要注意的是,拥有纯虚函数的类成为抽象类,是不能创建该类的对象的,只能用作基类来使用。
上一篇: String []split
下一篇: minGW的gdb调试