关于继承:C ++删除指向子类的指针时未释放内存

C++ Memory not freed when deleting pointer to subclass

我在代码中使用了动态内存分配,并且在尝试删除指向子类的指针时遇到了问题。我发现当我使用delete关键字时,最初分配的内存没有释放。该功能与原始基类一起工作正常。

这是一个问题,因为我在Arduino上运行代码,RAM很快就被吃掉,然后崩溃。

下面是一些示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Base
{
public:
    Base(){
        objPtr = new SomeObject;
    }
    ~Base(){
        delete objPtr;
    }
    SomeObject* objPtr;
};

class Sub : public Base
{
public:
    Sub(){
        objPtr = new SomeObject;
    }
};



// this works fine
int main()
{
    for (int n=0;n<100;n++) // or any arbitrary number
    {
        Base* basePtr = new Base;
        delete basePtr;
    }
    return 0;
}

// this crashes the arduino (runs out of RAM)
int main()
{
    for (int n=0;n<100;n++) // or any arbitrary number
    {
        Sub* subPtr = new Sub;
        delete subPtr;
    }
    return 0;
}

我认为它与基类中析构函数的语法有关。即使我为子类定制了一个析构函数,也会出现同样的问题。

有什么想法吗?


在C++中,构造函数在层次结构中被称为向上,也就是说,当您创建EDCOX1(7)时,Base()在EDCOX1×9之前执行。这意味着您运行了两次objPtr = new SomeObject;,只删除了一次。

您还应该使您的基类析构函数成为虚拟的,特别是如果您将从Baseptr中删除Derived实例。


应该使基类的析构函数为虚拟的。

1
2
3
virtual ~Base(){
    delete objPtr;
}


sub::sub()构造函数分配第二个额外的someObject在第一个由base::base()超级构造函数分配之后第二个分配的指针分配给objptr,引发泄漏。

注意:Base::Base()由Sub::Sub()隐式调用

解决方案:只需删除sub::sub()中不必要的分配

另一个建议:使您的基析构函数成为虚拟的,正如继承建议的那样。

1
2
3
virtual ~Base(){
    delete objPtr;
}

该指针的内存分配了两次。所以你永远不会为它释放你所有的新记忆。只需使指针成为私有成员,并只在基类中分配它。


当您创建派生类时,它首先调用Base类构造函数,然后调用Sub的构造函数,您可以在这里看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Base
{
public:
    Base(){
       std::cout <<"Base() ctor" <<std::endl ;
        objPtr = new int;
    }
    ~Base(){
        delete objPtr;
    }
    int* objPtr;
};

class Sub : public Base
{
public:
    Sub(){
       std::cout <<"Sub() ctor" <<std::endl ;
    objPtr = new int;
    }
};

所以你最后给new打了两次电话,一次在Base打,第二次在Sub打。在Base中分配的内存随后会丢失,并且出现泄漏。您还需要使析构函数virtual能够正确地处理派生类。