当委托构造函数抛出时,内存是否自动回收? [英] Is the memory automatically reclaimed when a delegating constructor throws?

查看:104
本文介绍了当委托构造函数抛出时,内存是否自动回收?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

接下来:委托构造函数抛出时调用析构函数?

  class X 
{
public:
X(){};
X(int):X(){throw std :: exception(); }
X(double){throw std :: exception(); }
〜X();
};

动态内存怎么办?通常,构造函数中的异常意味着对象没有被完全构造,因此内存被清理并且不会丢失。



但前面问题中的参数是对象是在代理完成后完全构建(或完全初始化)。这如何影响回收内存?我希望内存仍然清理!

  int main()
{
new X(5) // new called
//删除调用因为委托完成。
//我假设:
//内存重新声明(因为构造函数没有完成)
//我假设调整了C ++ 11标准来补偿。
//作为构造函数完成。
}

比较:

  int main()
{
new X(5.0); // new called
//删除** NOT **调用
//内存已重新声明(因为构造函数没有完成)
}

如果内存被清除,那么内存清理的定义需要从C ++ 03 spec。 c> $

如果由 new 调用的构造函数抛出一个异常,那么由 new 分配的内存将被自动释放。


如果上述对象初始化的任何部分 76 可以找到异常和合适的解除分配函数,调用解除分配函数释放正在构建对象的内存。



                                                                                                          >— C ++ 11 [expr.new] 5.3.4 / 18


对象初始化的任何部分描述包括构造函数调用和传递给构造函数的表达式的计算。



此外,这个行为在C ++ 98标准中有相同的规定[C ++ 98 5.4.3 / 17]。委托构造函数唯一的区别是,如果你的心理模型以前是基于完全构造的对象。






在第一个例子中:

  new X(5); 

事件顺序为:




  • 分配函数

  • X(int)调用




      • X(int)会抛出异常



      • 〜X()


    • 通过异常退出X(int)

    • 由于对象初始化失败而调用的释放函数

    • 异常继续正常传播



    使用第二个示例

      new X(5.0); 




    • 分配函数

    • X(double)

    • X(double)失败,但异常

    • 异常继续正常传播



    您可以通过替换分配和释放函数来观察此行为:

      #include< iostream> 
    #include< cstdlib>
    #include< stdexcept>
    #include< new>

    void * operator new(std :: size_t s){
    if(void * ptr = std :: malloc(s)){
    std :: cout< ; allocation\\\
    ;
    return ptr;
    }
    throw std :: bad_alloc {};
    }

    void operator delete(void * ptr)noexcept {
    if(ptr){
    std :: cout< deallocation\\\
    ;
    std :: free(ptr);
    }
    }

    struct S {
    S(){};
    S(int):S {} {throw std :: exception(); }
    S(double){throw std :: exception(); }
    〜S(){std :: cout<< destructor\ n; }
    };

    int main(){
    std :: cout< test 1 \\\
    ;
    try {
    new S(1);
    } catch(...){
    std :: cout< exception caught\\\
    ;
    }

    std :: cout<< test 2\\\
    ;
    try {
    new S(1。);
    } catch(...){
    std :: cout< exception caught\\\
    ;
    }
    }

    此程式的正确输出为:

      test 1 
    分配
    destructor
    deallocation
    捕获到异常
    test 2
    分配
    deallocation
    捕获异常


    Following on from this: Is the destructor called when a delegating constructor throws?

    class X
    {
    public:
        X()       {};
        X(int)    : X() { throw std::exception(); }
        X(double)       { throw std::exception(); }
        ~X();
    };
    

    What about dynamic memory? Normally an exception in the constructor means the object was not fully constructed and thus the memory is cleanedup and not lost.

    But the argument in the previous question is that the object is fully constructed (or fully initialized) after the delegate completes. How does this affect reclaiming the memory? I would hope that memory is still cleanedup!

    int main()
    {
        new X(5);        // new called 
                         // delete called because delegate completed.
                         // I assume:  
                         //      Memory re-claimed (because constructor did not complete)
                         //      I assume the C++11 standard adjusted to compensate.
                         //      As a constructor did complete.
    }
    

    Compared too:

    int main()
    {
        new X(5.0);      // new called 
                         //    Delete **NOT** called
                         // Memory re-claimed (because constructor did not complete)
    }
    

    If the memory is cleaned up, then the definition of when the memory is cleanup needs to be altered from C++03 spec. How is the behavior changed?

    解决方案

    If the constructor called by new throws an exception then the memory allocated by new is automatically deallocated. Delegating constructors change nothing in this regard.

    If any part of the object initialization described above76 terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed

                                                                                                           — C++11 [expr.new] 5.3.4/18

    The 'any part of the object initialization' described includes both the constructor calls and evaluation of the expressions passed to the constructor.

    Also, this behavior is specified identically in the C++98 standard [C++98 5.4.3/17]. The only difference delegating constructors make is if your mental model was previously based on the object being completely constructed or not. Given delegating constructors that's no longer equivalent to the actual specification of when deallocation occurs.


    In your first example:

    new X(5);
    

    The order of events is:

    • allocation function called
    • X(int) called
      • X() called (and exits successfully)
      • X(int) throws an exception
      • ~X() called
    • X(int) exits via exception
    • deallocation function called because object initialization failed
    • exception continues to propagate normally

    With the second example

    new X(5.0);
    

    • allocation function called
    • X(double) called
    • X(double) fails with an exception
    • deallocation function called because object initialization failed
    • exception continues to propagate normally

    You can observe this behavior by replacing the allocation and deallocation functions:

    #include <iostream>
    #include <cstdlib>
    #include <stdexcept>
    #include <new>
    
    void *operator new(std::size_t s) {
        if (void *ptr = std::malloc(s)) {
            std::cout << "allocation\n";
            return ptr;
        }
        throw std::bad_alloc{};
    }
    
    void operator delete(void *ptr) noexcept {
        if (ptr) {
            std::cout << "deallocation\n";
            std::free(ptr);
        }
    }
    
    struct S {
        S() {};
        S(int) : S{} { throw std::exception(); }
        S(double) { throw std::exception(); }
        ~S() { std::cout << "destructor\n"; }
    };
    
    int main() {
        std::cout << "test 1\n";
        try {
            new S(1);
        } catch(...) {
            std::cout << "exception caught\n";
        }
    
        std::cout << "test 2\n";
        try {
            new S(1.);
        } catch(...) {
            std::cout << "exception caught\n";
        }
    }
    

    The correct output of this program is:

    test 1
    allocation
    destructor
    deallocation
    exception caught
    test 2
    allocation
    deallocation
    exception caught
    

    这篇关于当委托构造函数抛出时,内存是否自动回收?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆