您的位置:首页 > 其它

virtual关键字

2015-11-04 22:01 246 查看
virtual的使用依托于类。


1、虚函数

被virtual修饰的成员函数称为虚函数。虚函数对于多态具有决定性的作用,有虚函数才能构成多态。我们知道,基类指针可以指向派生类对象(但是基类指针只能调用基类方法),但是派生类指针不能指向基类对象(为了避免派生类指针调用了基类中没有的派生类方法)。在基类中,被virtual修饰的成员函数(如print)在派生时,如果在派生类重写了该方法print,那么在调用该方法print的时候,将是根据对象的实际类型来决定是调用基类还是派生类中的该方法print。如下:

class Base
{
public:
virtual void print()
{
cout << "Base print" << endl;
}
};
class Derive : public Base
{
public:
void print()
{
cout << "Derive print" << endl;
}
};

int main()
{
Derive d;
Base * b = &d;
b->print();
//输出 "Derive print"
return 0;
}


测试结果表明:在调用print的时候,基类指针b实际指向的对象是什么类型(Derive),就调用哪个类型的print(Derive中的)。下面是一个更形象的例子说明,virtual所支持的多态性。

#include <iostream>
using namespace std;

//军队
class Troops{
public:
virtual void fight(){ cout<<"Strike back!"<<endl; }
};

//陆军
class Army: public Troops{
public:
void fight(){ cout<<"--Army is fighting!"<<endl; }
};
//99A主战坦克
class _99A: public Army{
public:
void fight(){ cout<<"----99A(Tank) is fighting!"<<endl; }
};
//武直10武装直升机
class WZ_10: public Army{
public:
void fight(){ cout<<"----WZ-10(Helicopter) is fighting!"<<endl; }
};
//长剑10巡航导弹
class CJ_10: public Army{
public:
void fight(){ cout<<"----CJ-10(Missile) is fighting!"<<endl; }
};

//空军
class AirForce: public Troops{
public:
void fight(){ cout<<"--AirForce is fighting!"<<endl; }
};
//J-20隐形歼击机
class J_20: public AirForce{
public:
void fight(){ cout<<"----J-20(Fighter Plane) is fighting!"<<endl; }
};
//CH5无人机
class CH_5: public AirForce{
public:
void fight(){ cout<<"----CH-5(UAV) is fighting!"<<endl; }
};
//轰6K轰炸机
class H_6K: public AirForce{
public:
void fight(){ cout<<"----H-6K(Bomber) is fighting!"<<endl; }
};

int main(){
Troops *p = new Troops;
p ->fight();

//陆军
p = new Army;
p ->fight();

p = new _99A;
p -> fight();

p = new WZ_10;
p -> fight();

p = new CJ_10;
p -> fight();

//空军
p = new AirForce;
p -> fight();

p = new J_20;
p -> fight();

p = new CH_5;
p -> fight();

p = new H_6K;
p -> fight();

return 0;
}


实验结果:


Strike back!

–Army is fighting!

—-99A(Tank) is fighting!

—-WZ-10(Helicopter) is fighting!

—-CJ-10(Missile) is fighting!

–AirForce is fighting!

—-J-20(Fighter Plane) is fighting!

—-CH-5(UAV) is fighting!

—-H-6K(Bomber) is fighting!

2、虚析构函数

实例化一个派生类对象的时候,首先将实例化基类对象,然后再实例化派生部分。同样,在销毁一个对象的时候,也希望首先调用派生类的析构函数,然后再调用基类的析构函数。如下:

//构造顺序和析构顺序
class Base
{
public:
Base()
{
cout << "Base" << endl;
}
~Base()
{
cout << "Base del" << endl;
}
};
class Derive : public Base
{
public:
Derive() :Base()
{
cout << "Derive" << endl;
}
~Derive()
{
cout << "Derive del" << endl;
}
};
int main()
{
Derive d;
return 0;
}


//结果:

Base

Derive

Derive del

Base del

但是,如果使用new运算符建立一个派生类临时对象(如Derive),并将该对象与基类指针关联时,那么在用delete回收动态内存的时候,将会发生一个情况:系统会只执行基类的析构函数,而不执行派生类的析构函数。

即根据上述(1、虚函数)的理论,在销毁动态内存中的派生类对象时,基类指针将调用基类的析构函数。如下:

//未用virtual修饰的基类析构函数
class Base
{
public:
Base()
{
cout << "Base" << endl;
}
~Base()
{
cout << "Base del" << endl;
}
};
class Derive : public Base
{
public:
Derive() :Base()
{
cout << "Derive" << endl;
}
~Derive()
{
cout << "Derive del" << endl;
}
};
int main()
{
Derive * d = new Derive;
delete d;

return 0;
}


结果:

Base

Derive

Base del

但是如果用virtual来修饰析构函数,那么基类指针将首先调用派生类的析构函数,再调用基类的析构函数。

class Base
{
public:
Base()
{
cout << "Base" << endl;
}
virtual ~Base()
{
cout << "Base del" << endl;
}
};
class Derive : public Base
{
public:
Derive() :Base()
{
cout << "Derive" << endl;
}
~Derive()
{
cout << "Derive del" << endl;
}
};
int main()
{
Derive * d = new Derive;
delete d;

return 0;
}


//结果:

Base

Derive

Derive del

Base del

基类的析构函数如果不用virtual修饰,则编译器将采用静态联编的方式,根据b的指针类型,调用相应类型的析构函数。而加上virtual后,则其后派生出来的所有类中的析构函数都为虚拟析构函数,此时编译器将采用动态联编的方式,即根据b指针实际指向的类型来决定它所调用的析构函数 。

总之,最好把基类的析构函数声明为虚函数,那么从该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不同。但是构造函数不能声明为虚函数。这是因为在执行构造函数时,类对象还未完成建立过程,当然谈不上函数与类对象的绑定。

3、纯虚函数

一种特殊的虚函数。在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。纯虚函数让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。定义示例如下:

class Base
{
public:
virtual void print() = 0;
//声明的后面加上“=0”注释表示此函数为纯虚函数
};


含有纯虚函数的类叫做抽象类。这种类不能实例化对象,只能作为基类为派生类服务。除非在派生类中完全实现基类中的所有纯虚函数,否则,派生类也是抽象类,不能实例化对象。

4、虚拟继承

即在继承的时候,除了指定公有/ 私有继承之外,可以另外通过virtual关键字来说明,在基类的多份继承中,将共享一份基类。如下:

class Base
{
public:
Base(){}
};
class Derive1 : virtual public Base
{
public:
Derive1() :Base(){}
};
class Derive2 : virtual public Base
{
public:
Derive2() :Base(){}
};


通过指定virtual继承,Derive1和Derive2将共享一份Base代码。如果未指定virtual继承,那么Derive1和Derive2将分别拥有一份Base代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: