【问题标题】:How is memory deallocated by delete operator with pointer to base class带有指向基类的指针的删除运算符如何释放内存
【发布时间】:2019-09-06 05:26:58
【问题描述】:

考虑这段代码:

class base{
    T* obj=new T[40];
    //...
public:
    base(){/*...*/}
    virtual ~base(){
        delete[] obj;
        //...
    }
    ...
};
class derived : public base{
    T* obj2=new T[20];
    //...
public:
    derived(){/*...*/}
    ~derived(){
        delete[] obj2;
        //...
    }
    ...
};

void func(){
    base&& exmp=giveder();    //giveder() returns derived
    base* dis=new derived[50];
    //...
    delete[] dis;
}

在上面的代码中,exmp 将被正确地破坏,因为析构函数被声明为虚拟的。但我的问题是dis 指向的免费存储是否会按预期释放,如果是,那么如何释放?

很明显sizeof(base)sizeof(derived) 是不同的。但这不会与exmp 混淆,但它会与dis 混淆吗?我认为它不起作用,因为无法从指向基址的指针中找出sizeof(derived),因此它无法确定需要释放多少字节。虽然我真的很想知道语言规范以及它是否合法。如果这是合法的,那么释放正在获取的东西的解决方法是什么?

一个附带的问题,数组不知道自己的大小(对吗?),那么析构函数中的delete[] objdelete[] obj2 将如何释放内存?

我是指针和内存管理的新手,所以我更喜欢描述性的答案。谢谢

【问题讨论】:

  • 所有数组的内存都会被正确释放。 new[] 存储delete[] 在释放内存时使用的簿记信息。无论对象在自动内存还是动态内存中,虚拟析构函数都被正确调用,并且在析构函数被调用后释放正确的字节数
  • @RemyLebeau,OP 的代码具有未定义的行为。
  • 为什么在你的类原型而不是构造函数中分配内存?
  • 数组不知道自己的大小,但内存管理器知道,因为它把那个大小存储在一个特殊的地方。
  • @SPlatten 如果它是真正的代码,那么我真的会在构造函数中分配内存(当然我可能希望用户将大小作为参数)

标签: c++ inheritance dynamic-dispatch


【解决方案1】:

您的代码具有未定义的行为。

可以用

base* ptr = new derived();
delete ptr;

但是不可以用

base* ptr = new derived[10];
delete [] ptr;

这是相关的文字from the C++11 Standard(强调我的):

在第一种选择(delete object)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为动态类型的基类。要删除的对象,并且静态类型应具有虚拟析构函数,否则行为未定义。 在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。

【讨论】:

    【解决方案2】:

    正如其他人已经注意到的那样:

    base* dis=new derived[50];
    delete[] dis;
    

    未定义的行为。例如,阅读这个问题以了解原因:Why is it undefined behavior to delete[] an array of derived objects via a base pointer?

    作为一种解决方案,我建议使用 唯一指针向量

    struct Base { virtual ~Base() = default; };
    struct Derived : Base {
      Derived() { std::cout << "Derived constructed...\n"; }
      ~Derived() { std::cout << "Derived destroyed...\n"; }
    };
    
    int main() {
      std::vector<std::unique_ptr<Base>> v;
      std::generate_n(std::back_inserter(v), 50,
                      []{ return std::make_unique<Derived>(); });
    }
    

    现场演示:https://wandbox.org/permlink/jcG71bDb1U7wsp2T

    当然,在这种情况下,Derived 对象不会被放置在连续的内存块中。

    【讨论】:

      猜你喜欢
      • 2017-03-01
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 2014-09-05
      • 1970-01-01
      相关资源
      最近更新 更多