1、含义
virtual关键字只用于类定义内部,出了类定义范围无法使用,即只能用于限定成员函数
定义为virtual的函数是基类希望派生类重新定义的,否则不能声明为virtual函数。
C++中的类成员函数默认为非virtual函数。
2、virtual与动态绑定
通过动态绑定我们能够在编写程序时使用继承层次中的任意对象,无需关心对象的具体类型。使用这些类的程序无需关心函数是在基类还是在派生类中定义。
在C++中通过基类的引用(或指针)调用虚函数时候,发生动态绑定,即引用(或指针)既可以是指向基类对象也可以是指向派生类对象。用引用(或指针)调用的虚函数在运行时确定被调用函数的实际定义。
触发条件:
只有指定为虚函数的成员函数才能进行动态绑定;
必须通过基类类型的引用或指针进行函数调用;
3、virtual与派生类
派生类一般会重新定义所有基类的虚函数(继承的),如果没有重新定义虚函数则默认使用基类中版本。
派生类中对于虚函数的声明必须与基类中完全一样(但若返回类型是对基类的引用或指针的虚函数,在派生类中可以返回基类函数所返回类型的派生类应用或指针)。
一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类可以保留virtual关键字。
4、virtual与构造、析构函数
C++明确指出:当派生类对象通过基类指针被删除,而该基类有non-virtual析构函数,其结果未定义——实际执行时候该派生类的派生成分未被销毁,只销毁了继承自基类的部分。这就发生了诡异的“局部销毁”,形成资源泄露。因此:任何有多态性或者含有virtual函数的基类都必须包含有一个virtual析构函数。
此外决不能在构造函数和析构函数中调用virtual函数,具体原因见参考文献【1】。
5、virtual与强制覆盖
某些情况下希望覆盖虚函数,强调使用虚函数的特定版本,这是可以使用作用域操作符:
base *p_base = &dreived;
T d = p_base->base_class::print_m();
这段代码强调对于func()的调用确定为base中定义的版本,这就使该调用在编译是确定。
如果某个基类只是为了让其他类继承而不会有或者不应该有类对象,可以在类中定义纯虚函数(pure virtual function)。包含纯虚函数的类为抽象类,抽象类除了作为抽象基类进行派生,抽象类不能创建对象。
class base {
public:
virtual void print_m() = 0;
}
上面定义的base为抽象类,func为纯虚函数。
#include
#include
#include
using namespace std;
class base
{
public:
virtual ~base(){
cout<<__func__<<"\n";
}
virtual void print_m(){
cout<<"IN Base: "<<__func__<<";Line "<<__LINE__<<"\n";
}
};
//=======================================================================
class son1 : public base
{
public:
son1(int mem):member(mem){ }
~son1() {
cout<<__func__<<"\n";
}
void print_m() {
cout<<"Here IN class son1,FUNC: "<<__func__<<" and its member="< v;
for (int i = 1; i <= 3; ++i)
{
v.push_back(get_base_ptr(i,i*i*i));
}
BOOST_FOREACH(base * &b,v)
{
b->print_m();
if(b!=NULL)
delete b;
}
return 0;
}
/*****************执行结果*************************
Here IN class son1,FUNC: print_m and its member=1
~son1
~base
Here IN class son2,FUNC: print_m and its member=8
~son2
~base
Here IN class son3,FUNC: print_m and its member=27
~son3
~base
[Finished in 0.6s]
**************************************************/
并且如果去掉基类中析构函数的virtual输出结果为:
/*****************执行结果*************************
Here IN class son1,FUNC: print_m and its member=1
~base
Here IN class son2,FUNC: print_m and its member=8
~base
Here IN class son3,FUNC: print_m and its member=27
~base
[Finished in 0.6s]
**************************************************/
可以看出如果没有基类的虚析构函数,在通过基类指针销毁派生类对象时候未能完全销毁。
此时基类为抽象类,如果给该基类声明对象
base b;
则会出现以下错误:/home/abing/software/clang_tool/test.cc: 在函数‘int main()’中:
/home/abing/software/clang_tool/test.cc:74:10: 错误: 不能将变量‘b’声明为具有抽象类型‘base’
/home/abing/software/clang_tool/test.cc:6:7: 附注: 因为下列虚函数在‘base’中为纯虚函数:
/home/abing/software/clang_tool/test.cc:15:18: 附注: virtual void base::print_m()
[Finished in 0.4s with exit code 1]
如果将基类中的:
virtual void print_m() = 0 ;
改为:
virtual void print_m(){
cout<<"IN Base: "<<__func__<<";Line "<<__LINE__<<"\n";
}
并且将
BOOST_FOREACH(base * &b,v)
{
b->print_m();
if(b!=NULL)
delete b;
}
改为:
BOOST_FOREACH(base * &b,v)
{
b->base::print_m();
if(b!=NULL)
delete b;
}
则执行结果为:
/*****************执行结果*************************
IN Base: print_m;Line 13
~son1
~base
IN Base: print_m;Line 13
~son2
~base
IN Base: print_m;Line 13
~son3
~base
[Finished in 0.6s]
**************************************************/
8、参考文献
[1] 《C++ Primer》第四版
[2] 《Effective C++》第三版
注:转载请说明出处http://blog.csdn.net/abingzhao/article/details/25548263