为什么会出现内存泄漏,当基类构造函数被调用?

时间:2019-02-02 18:58:09

标签: c++

我在练一些继承和虚析构函数和我不断收到运行我的代码,在子类的析构函数正常工作,但超类的析构函数只是不会正常工作后抛出,出现异常的问题。

我想我对析构函数有一些误解,因为每次我看到虚拟析构函数时,命令“ delete”实际上是在析构函数之外使用的,但是,如果我这样做了,那么创建析构函数的意义何在?< / p>

#include <iostream>


template <class T>
class List
{
protected:
    T *data;



public:
    List()
    {

        data = new T[5];
        std::cout << "List constructor!";
    }


    virtual void putIn()
    {
        for (int i = 0; i < 4; i++)
            std::cin >> data[i];
    }
    virtual void printOut()
    {
        for (int i = 0; i < 5; i++)
            std::cout << data[i] << std::endl;
    }
    virtual ~List()
    {
        delete[]data;
        std::cout << "List destructor!";
    }


};


template <class T>
class League : public List<T>
{
public:
    League()
    {

        data = new T[5];
        std::cout << "League constructor!";
    }


    virtual void putIn()
    {
        std::cout << "Enter your values:\n";
        for (int i = 0; i < 5; i++)
            std::cin >> data[i];
    }
    virtual void printOut()
    {
        std::cout << "Your values are:\n";
        for (int i = 0; i < 5; i++)
            std::cout << data[i] << std::endl;
    }


   ~League()
    {
        delete[]data;
        std::cout << "League destructor!";
    }

};



int main()
{
    List<char> *p;
    League<char> leag;

    p = &leag; 

    p ->putIn();


    getchar();
    getchar();




}

一切正常,但是程序结束时,它说抛出了异常,它指向基类的析构函数。任何帮助表示赞赏!

3 个答案:

答案 0 :(得分:2)

崩溃的摘要是:当派生类被破坏时,将调用其析构函数,然后将其基于基类的析构函数调用。派生的析构函数调用delete[]data;,然后基类再次调用delete[]data;,这是未定义的行为。幸运的是,在这种情况下,导致系统崩溃,所以你知道寻找它。

这个想法是基类拥有data指针,因此基类应该是要删除它的指针。派生类不拥有该指针,因此不应删除它。

由于这种误解,Dialectius还观察到内存泄漏。构造League时,将首先调用List构造函数,并分配内存,然后再调用League构造函数,其分配不同的内存,从而泄漏内存的第一位。同样,派生类不拥有该指针,因此可能不应该触摸它。派生类应要求基类修改指针。

值得一提的是,您的类违反了The Rule of Three,因此您应该添加复制构造函数和复制赋值运算符以确保类的安全。

答案 1 :(得分:1)

的问题是,data被删除两次,第一次在~League(),然后在~List()。从delete中删除~League()

然后存在内存泄漏问题。 LeagueList::data不负责,但会对其进行初始化。这会导致内存泄漏,因为以前的值将永远丢失并且不会被删除。因此,不要在League中设置新值,而要使用其他方法告诉List更改值,然后只有List负责其成员,并且必须delete[]设定一个新的前旧的。

答案 2 :(得分:1)

那我为什么需要子析构函数?

就像Dialecticus所说的,在这种情况下您不需要它。假设你要保持你的阵列备份,那么你就必须在子类中像T * childData另一个动态模板领域。现在,最好使用子类的析构函数。