【问题标题】:Could deleting complete class type result in undefined behavior?删除完整的类类型会导致未定义的行为吗?
【发布时间】:2017-01-23 00:19:03
【问题描述】:

我发现自己在理解 5.3.5$5 中从 C++ 标准中引用的以下句子时遇到了麻烦:(重点是我的)

如果要删除的对象在删除时具有 incomplete 类类型,并且 complete 类具有非平凡的析构函数或释放函数,则行为未定义.

我知道这个删除不完整类型的问题已经在 SO 中讨论过多次,我可以理解为什么删除不完整的类类型是未定义的行为。这个Q&A 解释得很好。

我无法理解的是关于完整类类型的部分。这是否意味着删除完整类的对象具有非平凡的析构函数或释放函数是未定义的行为?如果是这样,请提供一些代码来说明它可能导致的未定义行为。

【问题讨论】:

    标签: c++ undefined-behavior


    【解决方案1】:

    在有关情况下,未定义行为有两个先决条件:

    1. 正在通过指向不完整类型的指针删除对象;
    2. 被删除对象的完整类具有重要的析构函数或(用户定义的)释放函数。

    如果这些条件中的任何一个为假,则不存在未定义的行为(至少由于正在讨论的问题)。

    这尤其意味着

    1. 删除完整类型的对象是安全的(因为将执行正确的析构函数和/或释放函数)。

    2. 通过指向不完整类型的指针删除具有普通析构函数且没有用户定义的释放函数的对象是安全的(因为在没有完整类型信息的情况下,编译器不会调用析构函数并使用默认释放函数,它完美匹配通过指向完整类型的指针执行删除时会发生的情况)。

    【讨论】:

    • 另一种说法是,如果代码在指向不完整类型的指针上使用delete,编译器将不知道需要哪种清理,但会做出默认假设。如果该行为与编译器在知道需要什么的情况下会执行的操作相匹配,那么一切都会好起来的。如果行为不匹配,则标准对编译器是否将以默认方式运行、以类实际需要的方式运行、有时以非确定性方式执行其中一个或执行其他任何方式不施加任何要求编译器作者能想到的。
    • 没有所谓的“平凡的释放函数”。
    • @T.C.同意。固定。
    【解决方案2】:

    这是否意味着删除完整类的对象具有非平凡的析构函数或释放函数是未定义的行为?

    不,因为那意味着

    class Foo {
    public:
        ~Foo() { /*do something*/ };
    };
    

    是未定义的行为,事实并非如此。

    您似乎已经知道删除具有不完整类类型的对象是未定义的行为。问题是,它只是未定义的行为如果该类具有非平凡的析构函数/释放。

    在这种情况下不是未定义的行为:

    //Foo is forward declared somewhere and destructed
    //This is the class definition not available at the point that it is destructed
    class Foo {
    public:
        //~Foo() = default; 
    };
    

    仅在这种情况下未定义

    class Foo {
    public:
        ~Foo() { /*do something*/ };
    };
    

    请注意,即使 Foo 有一个普通的析构函数/释放,如果 Foo 继承自另一个具有非普通析构函数/释放的类,它仍然是未定义的行为。

    【讨论】:

    • 重要的是要注意,即使没有用户定义的析构函数类也可能具有非平凡析构函数(如果基类或成员之一具有非平凡析构函数)
    • @Revolver_Ocelot 你说得对,我忘了 :)
    【解决方案3】:

    让我试着用下面的代码解释一下我的理解:

    struct Foo;
    struct Bar;
    
    Foo* newFoo();
    Bar* newBar();
    
    int main()
    {
       // Get a pointer to Foo, somehow
       Foo* fp = newFoo();
    
       // Get a pointer to Bar, somehow
       Bar* bp = newBar();
    
       // Foo is an incomplete class at this point.
       // This is OK since Foo has a trivial destructor.
       delete fp;
    
       // Bar is an incomplete class at this point.
       // Not OK since Bar has a non-trivial destructor.
       // This is cause for undefined behavior.
       delete bp;
    }
    
    struct Foo {};
    
    struct Bar {
       Bar() { data = new int[10]; }
       ~Bar(){ delete [] data; }
       int* data;
    };
    
    Foo* newFoo()
    {
       return new Foo;
    }
    
    Bar* newBar()
    {
       return new Bar;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-12-23
      • 2011-01-31
      • 1970-01-01
      • 2014-08-11
      • 2016-09-04
      • 1970-01-01
      • 2015-01-16
      • 2021-01-26
      • 2013-04-24
      相关资源
      最近更新 更多