smallcraft的专栏

把精益求精当作一种习惯

C++中的虚函数

最近在做基于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();

阅读更多
个人分类: C/C++学习
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭