代码之家  ›  专栏  ›  技术社区  ›  Dpp

是否使用非虚拟析构函数和基类指针释放了整个对象?

  •  4
  • Dpp  · 技术社区  · 14 年前

    如果基类没有虚拟析构函数(例如为了避免vtable条目),并且派生类只有基本属性,那么当基类的指针被删除时,它是否释放new分配的所有内存?我知道派生类的析构函数不会被调用,但我想知道是否整个对象分配的内存将被释放? 我还假设对派生指针调用delete将释放整个内存空间。

    另外,如果它没有释放内存的派生类部分,那么它在基类中使用虚拟析构函数的情况下如何工作, 知道要释放多少内存 ?

    示例:

    class Base {
      public:
        int a;
        int b;
       Base() {}
      ~Base() {}
    };
    
    class Derived : public Base {
      public:
        int c;
        int d;
        Derived() {}
        ~Derived() {}
    };
    
    int main() {
      Base *p = new Derived();
      delete p; // is memory allocated for Derived freed?
      return 0;
    }
    
    6 回复  |  直到 14 年前
        1
  •  4
  •   Yakov Galka    14 年前

    这是不确定的行为,所以任何事情都有可能发生。引用标准[expr.delete]:

    在第一个备选方案(delete object)中,如果对象的静态类型 要删除的与它的 动态类型,静态类型应为 动态类型的基类 要删除的对象和 静态类型应具有虚拟 或者行为是 未定义。

    尽管它的工作方式是实现的细节,但典型的实现可能会自动重写派生类中的析构函数,并在那里实现内存释放。请注意,您必须在基类中定义虚拟析构函数,以便实现可以在虚拟表中保留一个条目。

        2
  •  3
  •   Edward Strange    14 年前

    从技术上讲,答案是“未知”。从没有虚拟析构函数的基指针中删除指向派生的指针是未定义的行为。

    实际上,大多数实现都无法正确删除派生对象中的内容。

        3
  •  3
  •   Stuart Golodetz    14 年前

    从形式上讲,这是未定义的行为,因此您无法保证内存被释放,或者您的程序确实执行了任何特定的操作。它可能会格式化你的硬盘。可能不会。实际上,在这种情况下,内存很可能会被释放——但如果你依赖它,那你就是个傻瓜,你不应该这么做。就这么简单。

        4
  •  0
  •   SingleNegationElimination    14 年前

    编辑:我错了:标准草案中的关键散文,强调我的:第5.3.5节

    3在第一个备选方案中(删除 对象),如果 要删除的对象不同于 它的动态类型,静态类型 应为动态的基类 要删除的对象的类型和 静态类型 应该有一个虚拟的 或者行为是 未定义。 在第二种选择中 (删除数组)如果 要删除的对象与 它的静态类型,行为是 未定义。

        5
  •  0
  •   Yttrill    14 年前

    内存很有可能被正确释放。仅仅因为标准不能保证这并不意味着它不会起作用。很难想象它会失败。

        6
  •  -4
  •   mjfgates    14 年前

    在您描述的非常特殊的情况下,当您没有虚拟析构函数时,派生类对象使用的所有内存都将被释放。(如果使用非虚拟析构函数 总是 导致派生类泄漏内存,我认为该语言不允许从使用它们的类继承!)

    记住,析构函数不是释放内存的工具;“delete”释放内存。析构函数只是一个被delete调用的函数。

    布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉。