多态的概念
C++多态即为:多态即为多种形式或形态,在编程语言中描述为同一种操作,可以有多种实现形式!
C++中多态产生的必要条件:
1、继承
2、要求有虚函数
3、要求有父类指针或引用指向派生类的对象
虚函数、纯虚函数
C++为实现多态引入虚函数、纯虚函数的概念!
虚函数:在类的函数前边加上 virtual 即可,这样这个函数就变为你想要override的函数!当你引用基类的指针或引用指向一个继承类的对象的时候,你调用一个虚函数,如果在子类重载了这个函数,你便是调用了子类中 这个函数的版本!
说明:对于基类中加virtual的函数,其所有的派生类中都不需要在加virtual,系统默认该函数就是虚函数,不用在显示的在函数前面在virtual。
纯虚函数:纯虚函数的定义 为虚函数 = 0 ;形如: virtual void shout() = 0 ;在虚函数后边加 =0;
说明:1、含有纯虚函数的类是抽象类,不可以实例化。
2、纯虚函数在基类中定义,在子类的实现,(子类中必须实现)。
如下代码:
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout<<"Base construct"<<endl;
foo();
}
virtual void foo()
{
cout<<"Base foo"<<endl;
}
virtual void fun1() = 0;
virtual ~Base()
{
cout<<"Base destruct"<<endl;
}
};
class Drive:public Base
{
public:
Drive()
{
cout<<"Drive construct"<<endl;
foo();
}
void foo()
{
cout<<"Drive foo"<<endl;
}
void fun1()
{
cout<<"fun1 instantiated"<<endl;
}
~Drive()
{
cout<<"Drive destruct"<<endl;
}
};
int main()
{
Base *ba = new Drive();
delete ba;
return 0;
}
说明:
1、对于纯虚函数 virtual void fun1() = 0; 假如说派生类中不对其实现,则不能编译通过。
2、含有虚函数的类的析构函数一般要 设置为 虚析构 函数,这样保证可以调用子类中的 析构函数。
3、抽象类不可以实例化 ,例如: Base *ba = new Base(); 不可以被编译通过。
4、C++的多态就是同一种操作,可以产生不同的形态。
在函数名前边 加入 virtual ,此函数就变为一个虚函数,虚函数的特点是,可以实现“动态联编”,它可以在运行时判断所指向的对象,并自动调用相应的函数!
对于虚函数与纯虚函数的区别:
首先:强调一个概念
定义一个函数为虚函数,不代表函数为不被实现的函数
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数
定义一个函数为纯虚函数,才代表函数没有被实现
定义他是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
对继承的影响:
普通的类(没有虚函数,纯虚函数)就可以被继承,而且工作的相当好 ,但是不能实现多态!
为什么要定义纯虚函数?
可以从两个方面考虑:
1.为了安全.因为避免任何需要明确但是因为不小心而导致的未知的结果.
2.为了效率,不是程序执行的效率,而是为了编码的效率。
binding
这里介绍下 动态binding 与 静态 binding
C++中支持两种多态:
1、编译时多态:函数运行前已经发生过的事件 函数重载、运算符重载(overload)等 静态binding
2、运行时多态:函数运行时发生的事件 虚函数机制 动态binding
函数banding
1、binding(绑定) : 将函数调用与函数体连接起来叫做绑定
2、预绑定(静态绑定):在程序运行之前执行,由编译器与链接器执行,编译时绑定
3、后绑定(动态绑定): 编译器在编译时未确定要调用那个函数,必须通过函数运行时所产生的信息来通知确定要调用那个函数;(动态绑定) ;运行时绑定
静态绑定与动态绑定的区别:
预绑定:
编译时多态(运行前多态,静态绑定) ,表现在以下几个方面:
1、对于在一个类中说明的重载(overload),编译系统根据重载函数的参数的个数、参数类型、参数的顺序的差别,来分别调用相应的函数。
2、对于基类和派生类中的重载函数,即时所带的参数完全相同,但由于他们属于不同的类,在编译时根据对象名前缀来加以区别,或者使用"类名:: ",也可以指示出编译器调用那个成员函数。
3、调用编译时多态时,优点在于高效率,因为编译系统可以在运行前对代码进行优化;缺点在与缺少灵活性,不足以满足程序对扩展性的要求
例1:编写 基类、派生类、派生类的派生类,在基类中定义虚函数,并全部实现,为每个类定义对象,观察每个对象的大小及内存的分配规律:
#include <iostream>
using namespace std;
//基类
class Base
{
//变量的权限问题,默认情况下是 类的 私有变量
int x;
public:
Base(){cout<<"default construct Base\n";}
Base(int n):x(n){ cout<<"construct Base\n"; }
~Base(){ cout<<"destructor Base\n"; }
virtual void setx(int n) {this->x = n; cout<<"in Base::setx\n";}
virtual int getx() { cout<<"in Base::getx\n";return this->x; }
} ;
//基类
class Derive:public Base
{
int x;
public:
Derive(){ cout<<"default construct Derive\n"; }
Derive(int x1,int x2):Base(x1),x(x2){ cout<<"construct Derive\n"; }
~Derive(){ cout<<"destructor Derive\n"; }
void setx(int n) {this->x = n; cout<<"in Derive::setx\n"; }
int getx() { cout<<"in Derive::getx\n"; return this->x; }
};
class subDerive : public Derive
{
int x;
public:
subDerive(){ cout<<"default construct subDerive\n"; }
subDerive(int n1,int n2,int n3):Derive(n1,n2),x(n3){ cout<<"construct subDerive\n"; }
~subDerive(){ cout<<"destructor subDerive\n"; }
void setx(int n) {this->x = n ; cout<<"in subDerive::setx\n"; }
int getx() { cout<<"in subDerive::getx\n"; return this->x;}
};
int main()
{
cout<<"sizeof Base : "<<sizeof(Base)<<endl;
cout<<"sizeof Derive : "<<sizeof(Derive)<<endl;
cout<<"sizeof SubDerive : "<<sizeof(subDerive)<<endl<<endl;
Base base(1);
Derive derive(2,3);
subDerive subder(4,5,6);
cout<<endl<<endl;
return 0;
}
运行结果: