C++虚析构函数

C++虚析构函数主要关注两个问题,何时要用虚析构以及它是怎么工做的。下面回答这两个问题。ios

问题1:何时要用虚析构函数?

答案:经过基类的指针来删除派生类的对象时,基类的析构函数应该是虚的。这样作是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。c++

缘由:用对象指针来调用一个函数,有如下两种状况:若是是虚函数,会调用派生类中的版本; 若是是非虚函数,会调用指针所指类型的实现版本。 析构函数也会遵循以上两种状况。当对象出了做用域或是咱们删除对象指针,析构函数就会被调用。当派生类对象出了做用域,派生类的析构函数会先调用,而后再调用它父类的析构函数,这样能保证分配给对象的内存获得正确释放。可是,若是咱们删除一个指向派生类对象的基类指针,而基类析构函数又是非虚的话, 那么就会先调用基类的析构函数(上面第2种状况),派生类的析构函数得不到调用。这样会形成销毁对象不彻底。bash

简要解释就是:析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。若是析构函数不是虚函数,而程序执行时又要经过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会形成销毁对象不彻底。函数

注意:并非要把全部类的析构函数都写成虚函数。由于当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增长类的存储空间。因此,只有当一个类被用来做为基类的时候,才把析构函数写成虚函数。ui

代码实例以下:this

class Base{
public:
    Base() { cout<<"Base Constructor"<<endl; }
 // ~Base() { cout<<"Base Destructor"<<endl; }
	 virtual ~Base() { cout<<"Base Destructor"<<endl; }
};
class Derived: public Base{
public:
    Derived() { cout<<"Derived Constructor"<<endl; }
    ~Derived() { cout<<"Derived Destructor"<<endl; }
};
int main(){
    Base *p = new Derived();
    delete p;
    return 0;
}
复制代码

未添加虚析构函数输出:spa

BaseConstructor

DerivedConstructor

BaseDestructor
复制代码

添加虚析构函数输出:指针

BaseConstructor

DerivedConstructor

DerivedDestructor

BaseDestructor
复制代码

问题2:C++中的虚函数是如何工做的?

虚函数依赖虚函数表进行工做。若是一个类中,有函数被关键词virtual进行修饰, 那么一个虚函数表就会被构建起来保存这个类中虚函数的地址。同时,编译器会为这个类添加一个隐藏指针(虚函数表的指针)指向虚函数表。若是在派生类中没有重写虚函数,那么,派生类中虚表存储的是父类虚函数的地址。每当虚函数被调用时, 虚表会决定具体去调用哪一个函数。所以,C++中的动态绑定是经过虚函数表机制进行的。当咱们用基类指针指向派生类时,虚表指针vptr指向派生类的虚函数表。 这个机制能够保证派生类中的虚函数被调用到。code

示例代码:对象

#include <iostream>
using namespace std;

class Shape{
public:
  Shape(){}
  Shape(int edge_length){
    this->edge_length = edge_length;
  }
  virtual ~Shape(){
    cout<<"Shape destructure."<<endl;
  }

  virtual int circumstance(){
    cout<<"circumstance of base class."<<endl;
    return 0;
  }

protected:
  int edge_length;

};

class Triangle: public Shape{
public:
  Triangle(){}

  Triangle(int edge_length){
    this->edge_length = edge_length;
  }

  ~Triangle(){
    cout<<"Triangle destructure."<<endl;
  }

  int circumstance(){
    cout<<"circumstance of child class."<<endl;
    return 3 * this->edge_length;
  }
};


int main() {
  Shape *x = new Shape();
  x->circumstance();
  Shape *y = new Triangle(10);
  int num = y->circumstance();
  cout<<num<<endl;

  delete x;
  delete y;

  return 0;
}

复制代码

运行结果:

circumstance of base class.
circumstance of child class.
30
Shape destructure.
Triangle destructure.
Shape destructure.
复制代码
相关文章
相关标签/搜索