程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

C++基类中虚析构函数

balukai 2025-05-28 15:27:09 文章精选 16 ℃

虚析构函数

C++中基类采用virtual虚析构函数是为了防止内存泄漏。假设派生类中申请了内存空间,需要在析构函数中释放内存;若基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。

下面是基类和派生类中都没有虚析构函数的一个实例:

#include <iostream>
using namespace std;
class Base
{
public:
  Base() //Base的构造函数
  { 
    name = new char[100];
    cout << "Base constructor" << endl; 
  }
  ~Base() //Base的析构函数
  { 
    delete name;
    name = nullptr;
    cout << "Base destructor" << endl; 
  }
  virtual void print()
  {
    cout << "Do something in class Base!" << endl;
  };
private:
  char* name;
};
class Derived : public Base
{
public:
  Derived() //Derived的构造函数
  { 
    name = new char[100];
    cout << "Derived constructor" << endl; 
  }
  ~Derived() //Derived的析构函数
  { 
    delete name;
    name = nullptr;
    cout << "Derived destructor" << endl; 
  }
  void print()
  {
    cout << "Do something in class Derived!" << endl;
  };
private:
  char* name;
};
int main()
{
  Derived* Test1 = new Derived(); //Derived类的指针
  Test1->print();
  delete Test1;
  cout << endl;
  Base* Test2 = new Derived(); //Base类的指针
  Test2->print();
  delete Test2;
  return 0;
}

运行结果:

从运行结果可以看出,当通过基类指针删除派生类对象时,派生类的析构函数根本没有调用,name指针创建之后没有删除,导致内存泄漏。原因时派生类重写了基类析构函数,虽然派生类和基类的析构函数名字不同,但是编译器对析构函数做了特殊处理。在内部,派生类和基类的析构函数名字是一样的。所以当基类的析构函数为非虚函数时,就不能构成多态,基类的析构函数隐藏了派生类的析构函数,所以只能调用基类的析构函数。

下面是基类定义虚析构函数的一个实例:

#include <iostream>
using namespace std;
class Base
{
public:
  Base() //Base的构造函数
  { 
    name = new char[100];
    cout << "Base constructor" << endl; 
  }
  virtual ~Base() //Base的析构函数
  { 
    delete name;
    name = nullptr;
    cout << "Base destructor" << endl; 
  }
  virtual void print()
  {
    cout << "Do something in class Base!" << endl;
  };
private:
  char* name;
};
class Derived : public Base
{
public:
  Derived() //Derived的构造函数
  { 
    name = new char[100];
    cout << "Derived constructor" << endl; 
  }
  ~Derived() //Derived的析构函数
  { 
    delete name;
    name = nullptr;
    cout << "Derived destructor" << endl; 
  }
  void print()
  {
    cout << "Do something in class Derived!" << endl;
  };
private:
  char* name;
};
int main()
{
  Derived* Test1 = new Derived(); //Derived类的指针
  Test1->print();
  delete Test1;
  cout << endl;
  Base* Test2 = new Derived(); //Base类的指针
  Test2->print();
  delete Test2;
  return 0;
}

运行结果:

从运行结果可以看出,定义了虚析构函数后,用一个基类的指针删除一个派生类的对象时,派生类的析构函数也被调用了。原因是当基类析构函数定义为虚函数后,删除对象时会直接调用派生类的析构函数,由于派生类析构时会先调用基类的析构函数,所以就把派生类和继承的基类都析构了。

总结:

  • 基类的析构函数不加virtual关键字:当基类的析构函数不声明成虚析构函数,派生类继承基类,基类的指针指向派生类时,delete删除基类的指针,只调动基类的析构函数,而不调动派生类的析构函数。
  • 基类的析构函数加virtual关键字:当基类的析构函数声明成虚析构函数,派生类继承基类,基类的指针指向派生类时,delete删除基类的指针,先调动派生类的析构函数,再调动基类的析构函数。
  • 由于基类的析构函数为虚函数,所以派生类会在所有属性的前面形成虚表,而虚表内部存储的就是基类的虚函数。
  • 当delete基类的指针时,由于派生类的析构函数与基类的析构函数构成多态,所以得先调动派生类的析构函数;之所以再调动基类的析构函数,是因为delete的机制所引起的,delete 基类指针所指的空间,要调用基类的析构函数。
最近发表
标签列表