c++ 没有虚拟析构函数可能会导致内存泄漏?

ncecgwcz  于 2023-07-01  发布在  其他
关注(0)|答案(4)|浏览(130)
#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对象??

0aydgbwb

0aydgbwb1#

可能吧
因为base没有虚析构函数,所以代码会表现出未定义的行为。什么事都有可能发生。它可能会像你期望的那样工作。它可能会泄漏内存。它可能会导致你的程序崩溃。它可能会格式化您的硬盘驱动器。
要求提供引证。C++11 §5.3.5/3规定,对于标量delete表达式(即,不是delete[]表达式):
如果要删除的对象的静态类型与其动态类型不同,则静态类型应是要删除的对象的动态类型的基类,并且静态类型应具有虚析构函数或行为未定义。
静态类型(base)与动态类型(derv)不同,静态类型没有虚析构函数,因此行为未定义。

wztqucjr

wztqucjr2#

在源代码中,没有内存泄漏,因为没有任何动态创建的成员变量。
考虑下面的修改示例情况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'动态创建的,而'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,以确保在删除指向派生类对象的基类指针时调用派生类析构函数。
我们可以说“当派生类有动态创建的成员时,析构函数必须是虚的”。

1wnzp6jl

1wnzp6jl3#

加入讨论。如果你要求GCC对析构函数没有定义为虚拟函数的代码进行内存清理,GCC会抛出一个错误。你需要用
-fsanitize=地址
作为编译选项。
比如说,

#include <cstdio>

class Base
{
public:
 Base()
 {
    printf("ctor Base\n");
 }

  ~Base()
 {
    printf("dtor Base\n");
 }
};

class Derived : public Base {
private:
  double val;

public:
 Derived(const double& _val)
 : val(_val)
 {
    printf("ctor Derived\n");
 }

 ~Derived()
 {
    printf("dtor Derived\n");
 }
};

int main() {
    Derived* d = new Derived(5);
    Base* p = d;
    // Derived destructor not called!!
    delete p;
    //delete d;
}

编译时将输出以下错误

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 1
=================================================================
==1==ERROR: AddressSanitizer: new-delete-type-mismatch on 0x602000000010 in thread T0:
  object passed to delete has wrong type:
  size of the allocated type:   8 bytes;
  size of the deallocated type: 1 bytes.
    #0 0x7fc6ed4db428 in operator delete(void*, unsigned long) (/opt/compiler-explorer/gcc-13.1.0/lib64/libasan.so.8+0xdc428) (BuildId: c9b24be17e4cbd04bdb4891782c4425e47a9259a)
    #1 0x401177 in main /app/example.cpp:38
    #2 0x7fc6ece58082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
    #3 0x4011fd in _start (/app/output.s+0x4011fd) (BuildId: 2751b7982ec6bd6f9c9092db77c9bfa142055e2d)

0x602000000010 is located 0 bytes inside of 8-byte region [0x602000000010,0x602000000018)
allocated by thread T0 here:
    #0 0x7fc6ed4da528 in operator new(unsigned long) (/opt/compiler-explorer/gcc-13.1.0/lib64/libasan.so.8+0xdb528) (BuildId: c9b24be17e4cbd04bdb4891782c4425e47a9259a)
    #1 0x40112f in main /app/example.cpp:35

SUMMARY: AddressSanitizer: new-delete-type-mismatch (/opt/compiler-explorer/gcc-13.1.0/lib64/libasan.so.8+0xdc428) (BuildId: c9b24be17e4cbd04bdb4891782c4425e47a9259a) in operator delete(void*, unsigned long)
==1==HINT: if you don't care about these errors you may set ASAN_OPTIONS=new_delete_type_mismatch=0
==1==ABO

参见https://godbolt.org/z/aaTrovaPE
但是,如果你在base上声明了一个虚析构函数,那么错误就会消失。

nue99wik

nue99wik4#

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

相关问题