一、多态性概述
使用同一个名字定义不同的函数,即使用同样的接口访问功能不同的函数。
eg:函数重载和运算符重载就实现了类的多态性
c++实现的多态有两种:
编译时多态:函数重载、运算符重载、模板;
运行时多态:借助虚函数实现。
二、静态联编和动态联编
联编:一个源程序需要经过编译、连接才能成为可执行的代码。上述过程中需要将一个函数调用链接上相应的函数代码(即在主函数中调用了某一函数,根据函数名及参数表找到相应函数体的过程),这一过程成为联编。
静态联编:在程序被编译时进行联编;(程序执行快,但灵活性小)
动态联编:编译时不能确定要调用的函数,在程序执行时联编;(灵活性高,程序执行慢,是c++实现运行时多态的关键因素)
三、虚函数
虚函数用 virtual关键字说明
eg: virtual float area(){
return 1;
}
格式: virtual 类型说明符 函数名 (参数表);
virtual 表示c++编译器对该函数的调用进行动态联编。
虚函数的作用
- 不使用虚函数时:
#include<iostream>
using namespace std;
class shape {
public:
float area() { return -1; }
};
class circle :public shape {
//公有继承shape,shape中的元素继承为circle中的公有成员;
float radious;
public:
circle(float r) {
//构造函数
radious = r;
}
float area() {
return 3.14159 * radious * radious;
}
};
int main() {
shape obj, * ptr;//基类shape的指针
circle c(3.6);//构造circle类的对象c
ptr = &obj;
cout << ptr->area() << endl;
ptr = &c;//shape类指针指向circle类
cout << ptr->area() << endl;//由于ptr是基类的指针,虽然指向c但调用的是shape类的area()函数
//若要是ptr指向c类后即可调用c类的area()函数则需要使用虚函数
return 0;
}
输出:
这种情况下为静态联编,两次ptr->area()调用的都是{return -1}的函数体,因此输出均为-1;
2.使用虚函数时:
#include<iostream>
using namespace std;
class shape {
public:
virtual float area() { return -1; }//关键字virtual说明虚函数
};
class circle :public shape {
//公有继承shape,shape中的元素继承为circle中的公有成员;
float radious;
public:
circle(float r) {
//构造函数
radious = r;
}
float area() {
return 3.14159 * radious * radious;
}
};
int main() {
shape obj, * ptr;//基类shape的指针
circle c(3.6);//构造circle类的对象c
ptr = &obj;
cout << ptr->area() << endl;
ptr = &c;//shape类指针指向circle类
cout << ptr->area() << endl;//由于ptr是基类的指针,虽然指向c但调用的是shape类的area()函数
//若要是ptr指向c类后即可调用c类的area()函数则需要使用虚函数
return 0;
}
输出:
这种情况下为动态联编,第二次调用的是circle类中的area()函数体;
- 虚函数必须作为类的成员函数,含有虚函数的类称为多态类。
- 使用虚函数在派生类中从新定义函数实现不同操作的方式称为函数超越或函数覆盖。
- 也可以使用对象名和点运算符的方式调用虚函数,即c.area(),但此时是静态联编,只有当访问虚函数需要通过基类指针S时才能得到运行时多态
虚函数与重载函数的比较
- 形式上,重载函数要求函数有相同的名称和不同的参数序列。而虚函数要求函数名称,参数序列及返回值都要相同;
- 重载函数可以是成员函数或非成员函数,而虚函数只能是成员函数。
- 对重载函数的调用是通过参数序列的不同作为调用不同函数的依据,虚函数是根据指针指向的对象不同来调用不同函数。
四、纯虚函数和抽象类
纯虚函数
指在基类中声明但是没有定义的虚函数,而且设置函数值等于0
eg:virtual type func_name(parameter list)= 0;
通过将虚函数设置为纯虚函数可以强制在派生类中重新定义虚函数(毕竟不重新定义根本没函数体可执行,o(* ̄︶ ̄*)o)
抽象类
- 包含有纯虚函数的类称为抽象类。
- 抽象类只能作为基类派生其他类。
- 抽象类的指针指向该抽象类的派生类的对象。
虚析构函数:通过基类指针删除派生类的对象。
#include<iostream>
using namespace std;
class A {
public:
virtual ~A() {
//虚析构函数
cout << "A::~A() called.\n";
}
};
class B :public A {
public:
B(int i) {
buffer = new char[i];
}
~B() {
//析构函数
delete[] buffer;
cout << "B:~B()called.\n";
}
private:
char* buffer;
};
void fun(A* a) {
delete a;
}
int main() {
B* b = new B(5);//b是B的一个指针对象,以5为参数
fun(b);//调用fun()函数 删除基类A的派生类B的对象b
}
输出:
五、模板与STL库
函数模板
定义了参数化的非成员函数,由编译器选择应该使用哪一种类型,并在模板中生成相应代码。
声明:关键字 template
template <class T>
T 函数名称(参数表){
函数体
}
eg: template <class T>
T max(T a,T b){
return a>b?a:b;
}
#include<iostream>
using namespace std;
template <class T>
T max(T a,T b) {
return a > b ? a : b;
}
int main() {
cout << "max(20,30)=" << max(20,30) << endl;
cout << "max('t','v')=" << max('t', 'v') << endl;
cout << "max(10.1,15.2)=" << max(10.1, 15.2) << endl;
return 0;
}
类模板
说明:
template <class T1,class T2....>
class 类模板名{
类模板定义;
}
标准模板库STL包括容器类、算法、迭代器。