【问题标题】:Where is the thrown object in c++?c++中抛出的对象在哪里?
【发布时间】:2012-07-01 12:11:26
【问题描述】:

我想弄清楚我抛出的对象在内存中的存储位置。所以我为它写了一个小程序:

#include <iostream>

#define print_dist() int a;\
    do { std::cout << __FUNCTION__ << "() a[" << (long)&a - (long)ptrMainStackBase << "]" << std::endl; } while (0)

#define print_distx(x) \
    do { std::cout << __FUNCTION__ << "() " #x "[" << (long)&x - (long)ptrMainStackBase << "]" << std::endl; } while (0)

#define print_distxy(x, y) \
    do { std::cout << __FUNCTION__ << "() " #x "(ex)[" << (long)&x - (long)y << "]" << std::endl; } while (0)

class CTest
{
    public:
        CTest()
        { std::cout << "CTest::CTest" << std::endl; }

//    private:
        CTest(const CTest&)
        { std::cout << "copy" << std::endl; }
};
const CTest *ptrException;
int *ptrMainStackBase;

void Test2()
{
    print_dist();
    CTest test;
    print_distx(test);
    std::cout << "&test=" << &test << std::endl;
    throw test;
}

void Test1()
{
    print_dist();
    try
    {
        Test2();
    }
    catch (const CTest& test)
    {
        ptrException = &test;
        print_dist();
        print_distx(test);
        print_distxy(test, ptrException);
        std::cout << "&test=" << &test << std::endl;
        throw test;
    }
}

int main()
{
    int b;
    ptrMainStackBase = &b;
    print_dist();
    try
    {
        print_dist();
        Test1();
    }
    catch (const CTest& test)
    {
        print_dist();
        print_distx(test);
        print_distxy(test, ptrException);
        std::cout << "&test=" << &test << std::endl;
    }

    return 0;
}

然后打印出来:

main() a[-4]
main() a[-8]
Test1() a[-64]
Test2() a[-104]
CTest::CTest
Test2() test[-108]
&test=0x7fffd3b21628 <- test created here on stack
copy
Test1() a[-68]
Test1() test[-140736732956164]
Test1() test(ex)[0]
&test=0xb89090 <- and copied here
copy
main() a[-12]
main() test[-140736732956020]
main() test(ex)[144]
&test=0xb89120 <- and here

看起来,当我抛出一个对象时,它首先被复制到另一个远离正常堆栈的堆栈。这是真的?以及为什么两个“异常栈帧”之间有144字节的距离?

【问题讨论】:

    标签: c++ exception


    【解决方案1】:

    当你抛出一个对象时,它确实首先被复制到某个临时位置。否则,堆栈展开将使其超出范围。然后通过引用捕获它会导致未定义的行为,如下所示:

    void foo() {
       A a;
       throw a;
    }
    
    void bar() {
       try {
          foo();
       } catch (A& a) {
          // use a
       }
    }
    

    除非a 已复制到某个临时位置,否则您将引用catch 中不再存在的变量。为此,A 必须具有公共复制构造函数(除非您使用的是 VS,在这种情况下为 it will use a private one as well...)。此外,这是通过引用捕获的一个很好的理由 - 否则,您将有两个复制结构而不是一个。

    【讨论】:

    • 我不敢相信!我认为通过引用捕获抛出的对象是正常的。
    • @Industrial-antidepressant,它是,而且应该——它是完全安全的,因为抛出的对象是被复制的。如果它没有被复制,你就会遇到问题。
    【解决方案2】:

    看起来,当我抛出一个对象时,它首先被复制到另一个远离正常堆栈的堆栈。这是真的吗?

    捕获异常时,抛出的test 不存在。原来的test 已经被破坏了。所以它必须是一个副本,并且新对象与参数和局部变量分开管理。换句话说,不在堆栈上。

    它住在哪里?这取决于实施。很可能是实现为您管理的动态内存(例如堆)。

    为什么两个“异常栈帧”之间有144字节的距离?

    标准没有说明当在catch 块中重新抛出异常时,实现如何处理异常。由于局部变量的throw 必须进行复制,因此实现throw 的最简单方法是始终进行复制。

    【讨论】:

      猜你喜欢
      • 2013-09-06
      • 1970-01-01
      • 2020-06-18
      • 1970-01-01
      • 1970-01-01
      • 2016-03-10
      • 2013-02-08
      • 2014-08-17
      • 2013-09-13
      相关资源
      最近更新 更多