如有兴趣了解更多请关注我的个人博客https://07xiaohei.com/
(一)概述:
1. 简介:
多态字面意思就是多种形态,可以简单地概括为“一个接口,多种方法”,多态性指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作,多态是面向对象编程领域的核心概念。
2. 目的:
封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了“接口重用”。也即,不论传递过来的究竟是类的哪个对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。
3. c++的应用:
当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++中的多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
c++中多态最常见的用法就是声明基类类型的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。
4. 分类:
C++支持两种多态性:编译时多态性,运行时多态性。前者被称为静态多态(也叫静态联编),通过重载函数和泛型编程实现;后者被称为动态多态(也叫动态联编),主要依赖虚函数体现。
两者的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。
一般情况下,我们说明的多态多数是指动态多态。
(二)静态联编:
1. 联编:
联编是指一个程序模块、代码之间相互关联的过程。
将源代码中的函数调用解释为执行特定的函数代码块的过程称为函数名联编。
同一个名称的函数有多种,联编可以把调用和具体的实现进行链接映射到某个最适合的函数。
2. 概念:
C++编译器在编译过程中完成的编译叫做静态联编,也就是早期匹配。因为这种联编实在程序开始运行之前就完成了。在程序编译阶段进行的这种联编在编译时就解决了程序的操作调用与执行该操作代码间的关系。
静态联编的效率高,但灵活性差。
3. 体现:
普通成员函数重载可表达为两种形式:
-
只在一个类说明中重载:
等价于一般函数重载,编译器可以根据其形参表进行区分。
-
基类的成员在派生类重载:
有三种编译区分方法:
- 根据参数的特征加以区分。(该方法实际上和上面的类似,只是可以都在任何一个继承层次上)
- 使用"::"加以区分。
- 根据类对象加以区分。
实际上,静态联编对函数的选择是基于指向对象的指针或者引用的类型。
#include<iostream>
using namespace std;
class A
{
private:
double a;
public:
A(double i = 0) :a(i) {}
void print()
{
cout <<"A1:" << "a:" << a << endl;
}
void print(bool)
{
cout << "A2:" << "a:" << a << endl;
}
};
class B:public A
{
private:
double b;
public:
B(double i = 0) :b(i) {}
void print()
{
cout << "B1:" << "b:" << b << endl;
}
void print(string)
{
cout << "B2:" << "b:" << b << endl;
}
};
int main()
{
A a;
B b;
a.print(); //A1
a.print(true); //A2
b.print(); //B1
b.print("1"); //B2
a.print("1"); //A2,强制类型转换
//b.print(true);//无法匹配
b.A::print(); //A1
b.A::print(true); //A2
b.B::print(); //B1
b.B::print("1");//B2
return 0;
}
(三)类指针的关系:
基类指针、引用和派生类指针、引用与基类对象和派生类对象有4种的可能匹配:
-
直接用基类指针、引用来引用基类对象。
-
直接用派生类指针、引用来引用派生类对象。
-
用基类的指针、引用来引用一个派生类对象:
只能访问从基类继承的对象,除非对这个基类指针进行强制类型转换成派生类指针,用派生类指针访问。
-
用派生类指针、引用来引用一个基类对象:
不安全,会越界访问,不被允许,派生类指针只有经过强制类型转换之后才能引用基类对象。
#include<iostream>
using namespace std;
class A
{
private:
double a;
public:
A(double i = 0) :a(i) {}
void print()
{
cout << typeid(this).name() << endl;
cout <<"A1:" << "a:" << a << endl;
}
void print(bool)
{
cout << typeid(this).name() << endl;
cout << "A2:" << "a:" << a << endl;
}
};
class B:public A
{
private:
double b;
public:
B(double i = 0) :b(i) {}
void print()
{
cout << typeid(this).name() << endl;
cout << typeid((A*)this).name() << endl;
cout << "B1:" << "b:" << b << endl;
}
void print(string)
{
cout << typeid(this).name() << endl;
cout << "B2:" << "b:" << b << endl;
}
};
int main()
{
A a(3);
B b(4);
A* pa = &a;
pa->print();
cout << endl;
B* pb = &b;
pb->print();
cout << endl;
pa = &b;
pa->print();
cout << endl;
B* pbb = (B*)pa;
pbb->print();
cout << endl;
A* paa = (A*)&pb; //A中数未初始化
paa->print();
cout << endl;
return 0;
}