使用花括号从堆栈中删除分配

时间:2019-02-21 16:00:44

标签: c++ mfc

假设我在一种方法中有很多堆栈分配。

如果我将花括号添加到一组代码中,则分配的对象超出范围时会从堆栈中弹出,还是在释放内存之前需要退出该方法?

我还应该补充一点,这是在MFC应用程序中完成的。

void LongMethod()
{
    {
        struct someLargeStruct;
        // Do a lot of work and allocations.
    }

    {
        struct anotherLargeStruct;
        // more work here.
    }   
}

2 个答案:

答案 0 :(得分:3)

C ++中没有 stack (至少,按照您的想法,它没有std::stack容器适配器,但完全不同)。

但是,有一个自动存储,该存储通常在堆栈上实现,但可能不是(例如,可以在寄存器中)。

但是,对于自动存储,

  

对象的存储空间分配在   封闭代码块并在末尾释放。所有本地对象   具有此存储期限,但声明为static,extern或   thread_local。

https://en.cppreference.com/w/cpp/language/storage_duration

这意味着,如果编译器决定使用堆栈存储变量,则应知道它们的存储在封闭块的末尾结束。尽管此时编译器没有义务减少堆栈指针,但如果不这样做,将是严重的实现质量问题。

答案 1 :(得分:3)

只是为了进一步澄清这一点-是的,该标准要求在块作用域的末尾释放自动存储,请参见[basic.stc.auto]/1

  

显式声明为register或未显式声明为staticextern的块范围变量具有自动存储持续时间。这些实体的存储将持续到创建它们的块退出为止。

但是,要求编译器仅实现程序(the as-if rule)的可观察的行为

  

...需要实现来模拟(仅)抽象机的可观察行为...

由于该标准将自动存储视为无限存储,并且没有其他方法可以观察堆栈的使用情况,因此,在符合规则的情况下,符合要求的编译器不必严格要求在每个作用域的末尾完全释放内存。

实际上,我们观察到GCC,clang和MSVC倾向于在函数启动时分配一次堆栈空间,而在函数退出时进行分配。尽管至少它们似乎在不同的块作用域之间重用了内存:

int do_something_with(int*);
int do_something_with(long long*);

void do_something()
{
    {
        int x[100];
        do_something_with(x);
    }
    {
        long long y[100];
        do_something_with(y);
    }
}

分配的堆栈帧:800 bytesxy共享相同的空间):

do_something():
        sub     rsp, 808
        mov     rdi, rsp
        call    do_something_with(int*)
        mov     rdi, rsp
        call    do_something_with(long long*)
        add     rsp, 808
        ret

在没有范围的情况下,行为会稍有变化,我们发现不再有内存重用:

int do_something_with(int*);
int do_something_with(long long*);

void do_something()
{
    int x[100];
    do_something_with(x);
    long long y[100];
    do_something_with(y);
}

分配的堆栈帧:1200 bytesx + y):

do_something():
        sub     rsp, 1208
        mov     rdi, rsp
        call    do_something_with(int*)
        lea     rdi, [rsp+400]
        call    do_something_with(long long*)
        add     rsp, 1208
        ret

因此,可以得出结论,是的,区块范围有一定的作用,但不要期望它与这些范围完全一致。

在递归函数(example)中尤其令人烦恼:

int do_something_with(long long*);
int bar();

void foo()
{
    {
        if (bar())
            foo(); // don't count on this being efficient
    }
    {
        long long y[10000];
        do_something_with(y);
    }
}

因此将大量堆栈用户隔离到单独的功能中会更加安全。