文章概述
1.多态出现的背景(赋值兼容性原则遇上函数重写);
2.多态理论联系实际;
3.多态成立的3个条件;
4.多态的理论基础;
5.多态的C++实现以及多态的实现效果;
6.虚析构函数;
多态出现的背景(赋值兼容性原则遇上函数重写)
a. 函数重写: 子类中定义与父类原型相同的函数;函数重写发生在父类与子类之间。
b. 如果子类中定义了与父类中原型相同的函数会发生什么?
父类中被重写的函数依然会被继承给子类,默认情况下父类的函数会被子类中重写的函数隐藏,通过父类名+::可以访问到父类中被隐藏的函数。
c. 赋值兼容性原则遇上函数重写:
class A
{
public:
int a;
public:
A()
{
a = 5;
cout << "A" << endl;
}
void printF()
{
cout << a << "A" << endl;
}
};
class B:public A
{
public:
int b;
public:
B()
{
b = 10;
cout << "B" << endl;
}
void printF()
{
cout << b << "B" << endl;
}
};
int main()
{
B b;
A* a = &b;
a->printF(); //调用的是A(父类)
return 0;
}
对于这个现象的分析:
C++是静态联编的语言。编译时,编译器自动的根据左边的类型判断是哪个类型的对象。
这个现象虽然没有错,但是不符合我们的新需求(根据对象的实际类型确定函数调用,如果父类指针指向的是子类对象,则会调用子类的函数;如果父类指针指向的是父类对象,则会调用父类的函数)。这时候产生多态(同样的调用语句产生不一样的表现效果)。
C++中通过virtual关键字对多态进行支持;使用virtual声明的函数被重写后即可展现多态特性。
多态理论联系实际
完整具体的分析上面现象的产生:
a. C++是静态编译类型的语言;
b. 编译的时候,编译器自动根据指针的类型判断指向的是一个什么样的对象,所以编译器认为父类指向的是父类的对象;
c. 由于程序没有运行,所以不知道父类指针指的是父类还是子类的对象。从安全的角度,C++编译器假设父类指针指向的是父类对象,因此编译的结果为父类的成员函数。这种特性就是静态编译。
多态成立的3个条件
(父类的同名函数中加virtual,子类的同名函数可以加也可以不加)
a. 要有继承:
b. 要有虚函数重写;
c. 父类指针(引用)指向子类对象。
class A
{
public:
int a;
public:
A()
{
a = 5;
cout << "A" << endl;
}
virtual void printF()
{
cout << a << "A" << endl;
}
};
class B:public A //继承
{
public:
int b;
public:
B()
{
b = 10;
cout << "B" << endl;
}
virtual void printF() //虚函数重写
{
cout << b << "B" << endl;
}
};
int main()
{
B b;
A* a = &b; //父类指针指向子类
a->printF();
return 0;
}
多态的理论基础
a. 联编: 一个程序模块,代码之间相互关联的过程。
b. 静态联编: 程序之间的匹配,连接在编译阶段实现,也称为早期绑定。函数重载就是静态联编的机制。
c. 动态联编: 程序联编推迟到运行时进行,又称为晚期绑定。if和swicth就是动态联编的机制。
多态的C++实现以及多态的实现效果
virtual关键字告诉编译器要支持多态,不要根据指针的类型判断如何调用函数,而是根据指针指向的实际对象类型判断如何调用函数。用virtual修饰的成员函数称为虚函数。虚函数分为两类: a.一般的虚函数; b.纯虚函数。
多态的表现效果: 同样的调用语句有多种不同的表现形态。
虚析构函数
(delete释放的是在堆上开辟的内存空间)
虚析构函数: 通过父类的指针释放所有的子类资源(将子类的析构函数全部执行一遍)
class A
{
public:
int a;
public:
A()
{
a = 5;
cout << "A" << endl;
}
virtual void printF()
{
cout << a << "A" << endl;
}
virtual ~A()
{
cout << "~A" << endl;
}
};
class B:public A //继承
{
public:
int b;
public:
B()
{
b = 10;
cout << "B" << endl;
}
virtual void printF() //虚函数重写
{
cout << b << "B" << endl;
}
~B()
{
cout << "~B" << endl;
}
};
int main()
{
B* b = new B;
A* a = b;
//通过父类的指针释放子类的资源,基类采用虚析构函数
delete a;
return 0;
}