【问题标题】:Possible memory leak without a virtual destructor?没有虚拟析构函数可能存在内存泄漏?
【发布时间】:2012-02-03 20:16:14
【问题描述】:
#include <iostream>
using namespace std;
class base
{
   int a;
 public: 
   base() {a =0;}
 };
 class derv :public base
 {
   int b;
  public:
   derv() {b =1;}
 };
 int main()
 {
    base *pb = new derv();
    delete pb;
 }

我在derv 类中没有虚拟析构函数,它只删除derv 对象的基础部分吗?

【问题讨论】:

  • 它是需要虚拟析构函数的基类。
  • @Mysticial:詹姆斯有这个。
  • @James,你说基类没有虚函数,但如果我们想继承基类,它必须有一个虚析构函数??

标签: c++ polymorphism dynamic-memory-allocation virtual-destructor


【解决方案1】:

可能会。

因为base 没有虚拟析构函数,所以您的代码表现出未定义的行为。任何事情都可能发生。它可能看起来像你期望的那样工作。它可能会泄漏内存。它可能会导致您的程序崩溃。它可能会格式化您的硬盘。

请求引用。 C++11 §5.3.5/3 指出,对于标量 delete 表达式(即,不是 delete[] 表达式):

如果待删除对象的静态类型与其动态类型不同,则静态类型应为待删除对象的动态类型的基类,并且 静态类型应具有虚拟析构函数,否则行为未定义。

静态类型 (base) 与动态类型 (derv) 不同,静态类型没有虚拟析构函数,因此行为未定义。

【讨论】:

  • 基类中缺少虚拟析构函数意味着派生类的析构函数中指定的任何自定义终结都不会执行。对象的内存仍将被正确释放。 (base 的析构函数也会被调用,如果它是由 non-virtual 定义的)。
  • @Xion:真的。行为未定义。所有的赌注都取消了。关于程序的行为,没有什么可以肯定地说。
  • @wilhelmtell:这是标准的。这是未定义的行为。
  • @wilhelmtell:如果你坚持的话。 :-)
  • 有趣。在实践中,UB 被宽松的编译器扫地出门的另一种情况。很高兴知道!
【解决方案2】:

您的代码中没有内存泄漏。如果您需要在派生类析构函数中释放一些内存,就会发生内存泄漏。

【讨论】:

  • 行为未定义。所有赌注都取消了。关于程序的行为,没有什么可以确定的。
【解决方案3】:

在您的源代码中没有内存泄漏,因为您没有任何动态创建的成员变量。

考虑下面案例 1 的修改示例:

#include <iostream>
using namespace std;
class base
{
   int a;
 public: 
   base() {a =0;}
   ~base() 
     {
       cout<<"\nBase Destructor called";

     }
 };
 class derv :public base
 {
   int *b;

  public:
   derv() { b = new int;}
  ~derv()
  {
      cout<<"\nDerv Destructor called"; 
      delete b;
  }
 };
 int main()
 {
    base *pb = new derv();
    delete pb;
 }

在这种情况下,输出将是,

   Base Destructor called

在这种情况下存在内存泄漏,因为 'b' 是使用 'new' 动态创建的,应该使用 'delete' 关键字将其删除。由于没有调用 derv 析构函数,因此它没有被删除,因此存在内存泄漏。

考虑下面的案例2:

#include <iostream>
using namespace std;
class base
{
   int a;
 public: 
   base() {a =0;}
   virtual ~base() 
     {
       cout<<"\nBase Destructor called";

     }
 };
 class derv :public base
 {
   int *b;

  public:
   derv() { b = new int;}
  ~derv()
  {
      cout<<"\nDerv Destructor called"; 
      delete b;
  }
 };
 int main()
 {
    base *pb = new derv();
    delete pb;
 }

在情况 2 的输出将是,

Derv Destructor called 
Base Destructor called

在这种情况下没有内存泄漏。因为调用了 derv 析构函数并且 b 被删除了。

在基类中可以将析构函数定义为Virtual,以确保在删除指向派生类对象的基类指针时调用派生类析构函数。

我们可以说“当派生类动态创建成员时,析构函数必须是虚拟的”。

【讨论】:

  • 行为未定义。所有的赌注都取消了。关于程序的行为,没有什么可以肯定地说。
  • @JamesMcNellis:在上面的代码中,派生类中有内存分配应该有虚拟析构函数。否则会出现内存泄漏。
猜你喜欢
  • 2012-09-02
  • 2019-11-09
  • 2015-07-12
  • 1970-01-01
  • 1970-01-01
  • 2017-08-08
  • 2016-04-02
  • 2013-11-28
  • 1970-01-01
相关资源
最近更新 更多