在多态中调用 3 层析构函数

Posted

技术标签:

【中文标题】在多态中调用 3 层析构函数【英文标题】:Calling 3 layers of destructors in polymorphism 【发布时间】:2016-04-05 00:17:55 【问题描述】:

我真的被这个问题困住了。我有一个内存泄漏的应用程序。为了摆脱这个问题,我需要为类调用析构函数。

对于 2 层类,这个问题是微不足道的,但在这种情况下,有 3 层类,我找不到任何方法来编译:

#include<iostream>

using namespace std;

/* *MUST* be an abstract class and contains data that must be freed */
class C  
public:
  virtual ~C()=0;
;

/* Contains concrete methods and contains data that has to be freed */
class B:public C  
public:
  virtual ~B();
;

/* contains no data, only methods nothing needs to be freed in here */
class A:public B 
public:
  ~A();
;

int main()
  C *c = new A;
  return 0;

当我编译代码时出现错误:

martyn@Hades ~ $ gcc deleteme.cpp -std=c++11
/tmp/cc7nxaw5.o: In function `main':
deleteme.cpp:(.text+0x12): undefined reference to `operator new(unsigned int)'
/tmp/cc7nxaw5.o: In function `__static_initialization_and_destruction_0(int, int)':
deleteme.cpp:(.text+0x4b): undefined reference to `std::ios_base::Init::Init()'
deleteme.cpp:(.text+0x62): undefined reference to `std::ios_base::Init::~Init()'
/tmp/cc7nxaw5.o: In function `B::B()':
deleteme.cpp:(.text._ZN1BC2Ev[_ZN1BC5Ev]+0x16): undefined reference to `vtable for B'
/tmp/cc7nxaw5.o: In function `A::A()':
deleteme.cpp:(.text._ZN1AC2Ev[_ZN1AC5Ev]+0x16): undefined reference to `vtable for A'
/tmp/cc7nxaw5.o:(.rodata._ZTV1C[_ZTV1C]+0x8): undefined reference to `__cxa_pure_virtual'
/tmp/cc7nxaw5.o:(.rodata._ZTV1C[_ZTV1C]+0xc): undefined reference to `__cxa_pure_virtual'
/tmp/cc7nxaw5.o:(.rodata._ZTI1C[_ZTI1C]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
collect2: error: ld returned 1 exit status

谁能想出一种方法来重写析构函数,以便 A、B 和 C 的析构函数都被调用?

我正在用这个撕掉我的头发,任何帮助将不胜感激。但是,我无法更改 cmets 或继承层次结构中的要求,这样做需要我重写整个应用程序。

非常感谢。

编辑:非常感谢下面的问题,对我有用的解决方案是:

#include<iostream>

using namespace std;

/* *MUST* be an abstract class and contains data that must be freed */
class C  
public:
  virtual ~C()=0;
;

C::~C()
  cout<<"destroying C"<<endl;


/* Contains concrete methods and contains data that have to be freed */
class B:public C  
public:
  ~B();
;

B::~B()
  cout<<"destroying B"<<endl;


/* contains no data, only methods nothing needs to be freed in here */
class A:public B 
public:
  ~A();
;

A::~A()
  cout<<"destroying A"<<endl;


int main()
  C *c = new A;
  delete c;
  return 0;

非常感谢您的建议。

【问题讨论】:

给你的析构函数定义,也许它会更好。 ~B() 另外,添加delete c; 实际上会调用它们。或者更好:使用智能指针。 这让我很困惑:/* *MUST* be an abstract class and contains data that must be freed */virtual ~C()=0;C 有免费数据。只有C 应该对C 有足够的了解才能做到这一点。 C 应该有一个析构函数。澄清。您可以拥有一个纯虚拟析构函数,但仍然提供析构函数。很奇怪,但确实如此。 大家好,我想你已经部分解决了我的问题,如果我在抽象类中指定析构函数并使其成为纯虚拟,那么现在可以使用,我可以让 B 和 C 析构函数调用,尽管我仍然无法在 A 中调用析构函数。不确定是否要更改问题。实际上,如果我现在使用这些新信息可能会更好。 哦等等,这完全解决了我的问题。我需要一个带有定义的基类 C 纯虚拟类,然后是非虚拟类 B 和 A,然后调用所有析构函数。我认为 B 必须是一个不纯的虚拟析构函数或其他东西。非常感谢大家:) 【参考方案1】:

这里的解决方案是在需要它们的类中实现析构函数,让编译器处理其余部分。

首先,C 有一些数据需要释放,所以 C 得到一个像这样的析构函数:

class C
    int* cp;

public:

    C() cp = new int; 

    virtual ~C()
        delete cp;
        cout << "~C()" << endl;
    

    virtual void someMethod()=0;
;

B 类应该实现纯虚函数,并且还有需要释放的数据,所以:

class B: public C
    int* bp;
    public:

    B() bp = new int; 

    virtual ~B()
        delete bp;
        cout << "~B()" << endl;
    

    virtual void someMethod()//some Implementation
    
;

最后,A 类只提供了一些方法,所以我们不需要析构函数。但我也会在此处包含一个用于演示:

class A: public B
    public:
    virtual ~A()
        cout << "~A()" << endl;
    
;

构造这个类并使用delete调用析构函数将正确调用所有三个析构函数:

int main()
    C *c = new A;
    delete c;
    return 0;

输出:

~A()
~B()
~C()

Try it online

请注意:在您发布的代码中,您似乎尝试使用纯虚拟析构函数的声明来使您的类抽象。但是如果你必须释放类中的资源,你需要使用另一个函数来达到这个目的,如果你没有纯虚函数的话。

编辑: 阅读here 可以了解到,为同一函数提供纯说明符和定义确实是有效的——这也适用于析构函数。所以你确实可以这样做:

class C
    virtual ~C() = 0;
;

C::~C() /*Implementation here*/

最后,只要至少一个函数是纯虚拟的 (=0),你的类就是抽象的。如果这是析构函数或其他一些函数也没关系。但是如果你的类需要释放资源,你需要一个体作为你的析构函数。

【讨论】:

感谢您的回答,我留下的问题之一是您是否可以对析构函数的虚拟继承进行分层,看起来您可以。非常感谢 :) 我将不得不用实际的代码库测试这些解决方案,因为 C 是一个抽象类至关重要,因为有一个多态类实例的向量,如果 C 不是抽象的,我会打开一个潘多拉魔盒。 在这种情况下,C 是抽象的——不是析构函数使它抽象,而是someMethod() 效果很好——它还修复了我的内存泄漏。谢谢你。我现在有一个无内存泄漏的神经网络!

以上是关于在多态中调用 3 层析构函数的主要内容,如果未能解决你的问题,请参考以下文章

浅谈多态基类析构函数声明为虚函数

浅谈多态基类析构函数声明为虚函数

c++ 析构函数 是在啥时候执行

在C++中,啥是运算符重载?啥是虚函数?

17. 虚析构函数再谈动态绑定多态到底是啥抽象类

C++ 类的多态四(虚析构函数的重要性)