最近在做基于libjingle的开发,发现里面有很多虚函数,于是就随便总结了一下虚函数的各用用法以及注意点.
1. 虚函数
虚函数其实讨论的是一个接口继承和实现继承的问题,讨论的什么时候只继承接口;什么时候同时继承接口和实现,可以覆写继承的实现;什么时候继承接口和实现,但不允许覆写任何东西。
1.1. 普通虚函数
声明一个普通虚函数可以实现覆写继承接口和实现。这个比较好理解,子类从基类那里继承了一个默认的实现,但是也可以重写.
1.2. 虚构造函数
构造函数可以是虚的吗?回答为:NO 。构造函数不是一个普通的函数,要构造一个对象,构造函数必须掌握所创建的对象的确切类型,因此构造函数不能是虚的。可以使用些Tack来实现,方法就是定义一个函数,由它调用构造函数并返回构造起来的对象。太棒了,创建一个对象而又不需要知道它的确切类型!!请看下面的实例:
class Base
{
public:
Base(); 默认构造函数
virtual Base* new_instance(){ return new Base();}
virtual Base* clone(){ return new Base(*this);}
}
class Derived:public Base{
public:
Derived();
Derived * new_instance(){ return new Derived();}
Derived * clone(){return new Derived(*this);}
}
应用:
void userfun(Base *p)
{
Base *p2 = p->new_instance();//创建了一个p2,但它的类型却是未知的
Derived *p3 = p->clone();//错误的
//覆盖函数的类型必须与它要去覆盖的那个函数的类型完全一样,除了返回值可以松动之外。
}
1.3. 虚析构函数
首先我们要搞清楚为什么要虚析构函数?这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。也就是只有当静态类型为基类,而动态类型为子类时,为了确保子类的析构函数能被调用,基类的析构函数必须被声明为虚析构函数。
class Base
{
public:
Base() {};
virtual ~Base() {};
};
class Derived : public Base
{
public:
Derived() {};
~Derived() { cout << "Output from the destructor of class Derived!" << endl; };
};
测试:
Base *pTest = new Derived;
delete pTest; /
这时” Output from the destructor of class Derived!”将被打印出来,也就是说子类的析构函数被调用了,但是如果将Base的析构函数前的virtual去掉,我们再运行测试程序,将会发出没有任何打印信息。
一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。
当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数
1.4. 纯虚函数
声明纯虚函数的方法:
virtual void purefun()=0;
声明一个纯虚函数pure virtual 的目的就是让Derived class只继承函数接口,因为它没有定义。但是实际上是可以有一份Defualt定义的,在使用时需要指出类名。如:
Derived->Base::Purefunction();
注意含有纯虚函数的类是不能被实例化的。如果Base类中有一个纯虚函数purefun(),Derived继承了Base后必须对purefun()进行重写,否则Derived也是一个函数纯虚函数的类,同样是不能被实例化的。
1.5. 非虚函数
声明一个Non-virtual函数,就是为了让Derived类继承函数的接口和一份强制性的实现。注意在基类中定义的非虚函数,就是希望Derived类不要去修改它。当然Derived如果要去修改它也是可以的,这时就叫做重写。不过这样的设计不好,如果需要修改,为什么不在Base中声明为虚函数呢?
1.6. 动态绑定
对象的静态类型就是它在程序中被声明时采用的类型;动态类型是指目前所指对象的类型。
Base*pb;
Base*pa = new Derived;
pa pb的静态类型都是Base*,但是pa的动态类型为Derived*
virtual函数属于动态绑定,调用一个Virtual函数时,究竟调用的是哪一份函数实现代码,取决于发出调用的那个对象的动态类型,但是virtual函数的缺省参数值却是静态绑定的,所以记住决不要重新定义继承而来的virtual 函数的缺省参数值
class Base
{
virtual display(int i =1)
cout<<”Base Display”<<endl;
}
class Derived :public Base
{
virtual display(int i = 2)
if(i ==2)
cout<<”derived i== 2” <<endl;
if(i == 1)
cout<<”derived i == 1” <<endl;
}
测试:
Base *pb;
Derived der;
pb = &der;
pb->display();