1.1引言
1.1.1 过程化(procedural programming):
程序被认为是在一个数据集合上进行的一系列操作。
1.1.2 结构化(structured programming):
主要思想是:功能分解并逐步求精。
1.1.3 面向对象的程序设计(object_oriented programming):
实质是把数据和处理这些数据的过程合并为一个单独的“对象”――一个具有确定特性的自完备的实体。
1.1.4 面向对象程序设计的特征:
1) 封装
2) 继承
3) 多态
1.2类与数据封装
1.2.1 什么是类?
简单的说,类就是一种用户定义的数据类型,跟结构类似;并且,类具有自己的成员变量和成员函数(方法),通过它们可以对类自身进行操作。如:汽车可以看作是发动机、车轮、座椅等诸如此类的集合。也可以从功能的角度来研究,譬如,能移动,加速,减速,刹车等。
例如:
class CMyClass1
{
protected:
CMyClass1();
public:
virtual ~ CMyClass1();
}
1.2.2 封装(encapsulation)
定义:指能够把一个实体的信息、功能、响应都装入一个单独的对象中的特性。封装的优点如下:
1) 封装允许类的客户不必关心类的工作机理就可以使用它。就象驾驶员不必了解发动机的工作原理就可以驾驶汽车一样,类的客户在使用一个类时也不必了解它是如何工作的,而只需了解它的功能即可。
2) 所有对数据的访问和操作都必须通过特定的方法,否则便无法使用,从而达到数据隐藏的目的。
1.2.3 对象
对象就是类的实例。类与对象的关系就如类型和变量的关系,所有对类的操作都必须通过对象来实现。当一个类定义了多个对象时,每个对象拥有各自的成员数据。
1.2.4 类的三种成员类型
1) 私有成员(private):缺省情况下,一个类中的所有成员都是私有的。私有成员只能被类本身的成员函数访问。并且不具有继承性。
2) 公有成员(public):公有成员可以被类成员函数和外部函数使用。
3) 保护成员(protected):类的保护成员能被类及其派生类的成员函数和友员函数使用,具有继承性。
1.2.5 构造函数与析构函数
1)构造函数
a. 是特殊的成员函数;在创建对象时首先由系统自动调用。它的作用是为新创建的对象分配空间,或为该对象的成员变量赋值等;
b. 构造函数名必须与其类名称完全相同,并且不允许有返回值。
2)析构函数
a. 析构函数是构造函数的逆操作;
b. 析构函数在类名之前加~来命名,它不允许有返回值,也不允许带参数,并且一个类只能有一个析构函数。
1.3继承
1.3.1 传统程序设计的缺点:
增加功能对程序所作的修改工作量非常大。
1.3.2 继承的优点:
继承的方法允许在不改动原程序的基础上对其进行扩充,这样使得原功能得以保存,而新功能也得以扩展。这有利于减少重复编码,提高软件的开发效率。
1.3.3 基类与派生类
1)一个类可以继承其它类的成员,被继承的类叫基类或父类;继承类叫派生类或子类
2)派生类不但拥有自己的成员变量和成员函数,还拥有父类的成员变量和成员函数。
1.3.4 类的保护成员(protected)
前面介绍了类的私有成员只能被类的成员函数和友员函数使用;类的保护成员能被类及其派生类的成员函数和友员函数使用。也就是说,类的保护成员具有继承性,而类的私有成员不具有继承性。
1.3.5 公用基类和私有基类
1)公用基类中的所有public成员在派生类中仍是public成员,所有protected成员在派生类中仍是protected成员。
2)私有基类中的public成员和protected成员在派生类中均变成private成员。
1.3.6 多重继承
1)多重继承的定义方法
例子:
class A
{
…
public:
int i;
void func1();
…
};
class B
{
…
public:
int i;
void func1();
…
};
class C: public A,B
{
…
void Show()
…
};
缺省情况下基类被定义为 private;因此基类B为私有基类。
2)继承的不确定性
例子:
class C:public A,B
{
…
void Show()
{
j = i*i;
func1();
}
…
};
由于基类A和B中同时拥有数据成员i和成员函数func1,类C引用基类的成员时,系统无法分辨是调用哪一基类的成员而发生错误;
3)解决多重继承的不确定性:
使用域操作符指明要调用的基类,即可解决不确定性问题。
class C:public A,B
{
…
int j;
void Show()
{
j = A::i*B::i;
A::func1();
}
…
};
1.3.7 多层继承
定义:所谓多层继承指的是从一个类派生出另一个类,然后以派生类作为基类,派生出另一个类,直到最后生成的派生类满足需要为止(见MSDN中的Hierarchy Chart)。
1.3.8 派生类的构造函数与析构函数
在继承关系下,派生类的构造函数负责调用基类的构造函数来设置基类数据成员值。
例:
class base
{//基类
…
public:
int i;
base(int j)
{//构造函数
i = j;
}
…
};
class derived:public base
{//派生类
…
public:
double f;
derived(int, double);
…
};
derived::derived(int k, double l):base(k)
{//派生类构造函数
…
f = l;
…
}
1.3.9 构造函数的调用顺序
1) 在定义派生类对象时,系统首先调用基类的构造函数,然后调用派生类的构造函数;在上例中,derived类首先调用base类的构造函数,然后调用自身的构造函数。
2) 析构函数的调用顺序与构造函数的调用顺序相反。
1.4重载
1.4.1函数重载
1)如果函数有相同的名称和返回值,而有不同的参数个数或参数类型,则这些函数就是重载函数。
2)派生类继承了基类的某一函数,并且又自定义了一个同名函数,有相同的返回值,不同的参数类型或参数个数。这种情况不属于重载。因为它们属于不同的域。
3)例:
class base
{
…
void func(int i)
{
…
}
void func(double f)
{
…
}
void func(double f, long q)
{
…
}
…
};
1.4.2 操作符重载
重载操作符的定义:返回值类型 operator op (参数表);其中,op为重载操作符,它必须是VC++中所定义的运算符。然后像定义函数一样定义重载操作符函数。
例子:
class person
{
…
int age;
void operator ++();
…
};
void person::operator++()
{
age++;
}
1.5虚拟函数与多态性
多态性是面向对象程序设计的精髓之所在,也是C++中最难理解和掌握的部分。在C++中,多态性是建立在虚拟函数基础上的,虚拟函数的使用使类的成员函数表现出多态性。
1.5.1虚拟函数
1)函数的定义:在定义类时在其成员函数前加上关键字virtual;
2)如果基类中成员函数定义为虚函数,则派生类中与其定义完全相同的成员函数,编译器自动将其视为虚函数;
3)只有类的成员函数才能定义为虚函数。
4)虚拟成员函数的存取要看首次定义它的类中,该函数是public还是private。
例:
class Insect
{
…
virtual bool CanFly();
…
};
bool Insect :: CanFly()
{
return FALSE;
}
class Butterfly:public Insect
{
…
bool CanFly();
…
};
bool Butterfly :: CanFly()
{
return TRUE;
}
1.5.2 虚函数的调用
1) 根据对象的不同而去调用不同类的虚拟函数
2) 可以使用基类对象调用派生类对象,即将派生类对象或指针赋值给基类对象或指针。
3) 反方向的赋值(将基类的对象或指针赋给派生类的对象或指针)是危险的。
例:
bool rtn;
Insect inc1,*pInc;
Butterfly btfly;
pInc = &inc1; //pInc指针指向Insect对象
rtn = pInc->CanFly(); //返回FALSE
pInc = &btfly; //pInc指针指向Butterfly对象
rtn = pInc->CanFly(); //返回TRUE
1.5.3 虚拟函数与重载函数的区别
1)形式上,重载函数要求有相同的返回值类型和函数名,并有不同的参数序列;而虚拟函数要求三者完全相同。
2)重载函数可以是成员函数或非成员函数;而虚拟函数必须是成员函数。
3)调用方法上,重载函数根据所传递的参数序列的差别作为调用的依据;而虚拟函数则根据调用对象的不同而去调用不同类的函数。
4)虚拟函数在运行时表现出多态功能;而重载函数不具有这一功能。
1.5.4纯虚函数
定义:virtual type funcname(parameter)=0;
C++中有时设计基类就是为了被继承,而基类中的虚拟函数不做任何工作,这种情况下可以将基类中的虚拟函数定义为纯虚函数。包含纯需函数的类叫抽象类。抽象类不能定义对象,但可以定义指向它的指针。
[此贴子已经被作者于2006-8-29 8:52:13编辑过]