面向对象编程:多态性(C++)

面向对象编程:多态性(C++)

一、简介

1、介绍:同样的消息给各种不同的对象时,会产生多种形式的结果,这就是多态性。

注:
(1)、继承层次中的对象
(2)、消息是重载的函数 (因为重载的函数的签名才会一样,即函数名和类型)

2、多态性能设计和实现更具扩展性的软件系统
3、多态性的两个底层技术:虚函数动态绑定

(一)、静态绑定:系统在编译时就确定如何实现某一动作;
(二)、动态绑定:系统在运行时动态实现某一动作(在编译时不确定执行哪段代码)。

4、多态发生的两个要素:
(1)、虚函数的调用;
(2)、指向派生类对象或引用的基类指针(它们不同时刻指向不同派生类的对象)。

二、类继承层次中对象之间的关系

1、派生类对象可以被当做它的基类对象来处理。

不能把基类对象当做任何一个派生类的对象来处理。

GraduateStudent gs;//实例化一个派生类对象gs
Student s = gs;//基类对象指向派生类对象
Student &t = gs;//基类的引用可以指向派生类
Student *p = &gs;//基类的指针可以指向派生类对象

gs = s;//错误,不可以派生类对象指向基类对象
GraduateStudent *pGs = &s;//错误,派生类指针不可以指向基类对象

2、调用的函数功能是由调用函数的句柄(指针或引用)决定的,而不是由句柄所指向的对象类型决定。
例如:

/*一个基类指针指向派生类*/
commissionEmployeePtr = &basePlusCommissionEmployee;//commissionEmployeePtr为基类指针,basePlusCommissionEmployee为派生类对象
commissionEmployeePtr -> print();//print函数功能是由基类决定的,不是派生类。找句柄所属类的对象。

3、通过基类指针调用派生类的成员函数:

1、基类指针指向派生类对象,并试图访问只在派生类中拥有的成员函数,会产生编译错误(派生类中有的成员函数基类中没有)。
2、显示把基类指针强制转化为派生类指针(向下强制类型转换),可以通过指向派生类对象的基类指针访问只在派生类中拥有的成员。

例如:

/*getBaseSalary(),setBaseSalary()为派生类中的函数;commissionEmployeePtr为基类指针*/
double baseSalary = commissionEmployeePtr ->getBaseSalary();
commissionEmployeePtr -> setBaseSalary(500);//两条语句错误。

以上情况可以用强制类型转换的方法,把基类指针commissionEmployeePtr转换成派生类指针。

4、虚函数(动态绑定)
利用虚函数,调用哪个版本的虚函数就由句柄所指向的对象类型决定,而非句柄的类型。

虚函数的定义在函数原型前加关键字virtual.函数原型和实现都加关键字。

注意:动态的绑定只发生句柄必须是基类的指针或引用发送消息的函数是虚函数;如果是一个确定的基类对象和派生类对象,没有动态的性质。

举例:

/*以下print()函数全是virtual函数*/
commissionEmployeePtr = &commissionEmployee;//基类指针指向基类对象
commissionEmployeePtr -> print();//此处的print()自然找基类的print()执行

basePlusCommissionEmployeePtr = &basePlusCommissionEmployee;//派生类指针指向派生类对象
basePlusCommissionEmployeePtr -> print();//print()找派生类中的print()操作

commissionEmployeePtr = &basePlusCommissionEmployee;//基类指针指向派生类对象
commissionEmployeePtr -> print();//此处print()将调用派生类中的print()函数

(1)、派生类中重载的函数必须和在基类中的具有相同的原型,如以上的print函数。
(2)、用虚函数进行 动态绑定,只能通过指针或引用句柄来实现。
(3)、一旦一个函数声明为virtual,那么从整个继承层次的那一点向下的所有类中,它将保持virtual的,即使当派生类重写此函数时并没有显示的将它声明为virtual.
(4)、当派生类选择不重写从其基类继承而来的virtual函数时,派生类就会简单地继承它的基类的virtual函数的实现。

三、抽象类和纯virtual函数

1、抽象类:有些类通常在类的继承层次结构中作为基类,从来不实例化任何对象,这种类称为抽象类。

注:
(1)、抽象类是不完整的,派生类必须定义缺少的部分。
(2)、构造一个抽象类的目的就是为其它的类提供合适的基类。
(3)、能实例化对象的类称为具体类,具体类实现了它所定义的每一个成员函数。

2、通过声明类的一个或多个virtual函数为纯虚函数,就能定义其为抽象类

纯虚函数定义:
virtual void draw() = 0;

3、纯虚函数不提供函数的具体实现每个派生的具体类必须重写所有类的纯虚函数的定义
4、抽象类为类层次结构中的各种类定义公共的接口。抽象类包含一个或多个纯虚函数,这些函数必须在派生类中重写。

(1)、试图实例化抽象类的对象将导致编译错误。
(2)、未能在派生类中重写纯virtual函数,然后试图实例化这个派生类对象将产生编译错误。

四、应用向下强制类型转换

1、向下强制类型转换

BasePlusCommissionEmployee *derivedPtr = dydynamic_cast<BasePlusCommissionEmployee *>
(employee[i]);

返回值:derivedPtr不为零,说明其指向一个BasePlusCommissionEmployee 对象
否则,它指的不是这种BasePlusCommissionEmployee 的对象。

返回运算符操作数类型的信息(要写出类型的名字)

cout << typeid(int).name() << endl;int a ;
cout << typeid(a).name() << endl;
输出的都是int

五、virtual析构函数

1、将基类的析构函数声明为虚析构函数,这样就会使所有派生类的析构函数自动成为虚析构函数(即使它们与基类析构函数名不同)。
2、当基类指针指向其派生类对象,用delete删除基类指针时,就会调用其所指对象(即其对应派生类)的析构函数,所有类的析构函数将执行,如果派生类要求执行析构函数,那么基类必须声明为虚析构函数

class A 
{
 public:
       A()
       {}
       virtual ~A();
};
A::~A()
{
       cout << "~A"<< endl;
}
class B:public A
{
public:
       B(int);
       virtual~B();
private:
       int *b;
};

B::B(int n)
{
   b = new int(n);//实例化B类对象
}

B::~B()
{
   cout << "~B" << endl;
   delete b;//要回收开辟的
}

在继承层次关系中,在某一层出现开辟空间的操作,则要把析构函数声明为虚析构函数。

3、构造函数不能是virtual函数。

六、(补充)类模板和模板类

1、类模板和模板类
所谓类模板,实际上是建立一个通用类,其数据成员、成员函数的返回类型和形参类型不具体指定,用一个虚拟的类型来代表。
使用类模板定义对象时,系统会根据实参的类型(模板实参)来取代类模板中虚拟类型从而实现了不同类的功能。

2、定义一个类模板,格式如下:

template<typename Type>
class 类名
{
   类成员函数
}

1template是一个声明模板的关键字
2、Type是类型参数(自定义)

或
template<class Type>
class 类名
{
   类成员函数
}

例如:建立一个用来实现求和的类模板
在这里插入图片描述
类模板定义对象时用以下形式

类模板名<实际类型名> 对象名[{实参表列}]

int main()
{
   Three <int> sum3_1(3,5,7);//用类模板定义对象sum3_1,此时T被int取代
   Three <double> sum3_2(12.34,34.56,56.78);//用类模板定义对象sum3_2,此时T被double取代
   cout << "三个整数之和是" << sum3_1.sum() << endl;
   cout << "三个双精度数之和是:" << sum3_2.sum() << endl;

return 0;

3、类模板中的成员函数可以在类模板体外定义。此时,若成员函数中有类型参数存在,则C++有一些特殊的规定:
(1)、需要在成员函数定义之前进行模板声明;

(2)、在成员函数名前缀上“类名<类型参数>::”
在类模板体外定义的成员函数的一般形式如下:

template <typename 类型参数>
函数类型 类名<类型参数>::成员函数(形参表)
{
  ...........
}

例如,上例这种成员函数Three在类模板体外定义时,应该写成:

template<typename T>
T Three<T>::sum()
{ 
   return x + y + z;
}
发布了10 篇原创文章 · 获赞 227 · 访问量 3978

猜你喜欢

转载自blog.csdn.net/m0_46518461/article/details/105647337